29 #ifndef CEREAL_ARCHIVES_XML_HPP_ 30 #define CEREAL_ARCHIVES_XML_HPP_ 34 #include "cereal/external/rapidxml/rapidxml.hpp" 35 #include "cereal/external/rapidxml/rapidxml_print.hpp" 36 #include "cereal/external/base64.hpp" 50 #ifndef CEREAL_XML_STRING_VALUE 54 #define CEREAL_XML_STRING_VALUE "cereal" 55 #endif // CEREAL_XML_STRING_VALUE 63 return c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r';
117 explicit Options(
int precision = std::numeric_limits<double>::max_digits10,
119 bool outputType =
false ) :
120 itsPrecision( precision ),
122 itsOutputType( outputType ) { }
139 itsOutputType( options.itsOutputType ),
140 itsIndent( options.itsIndent )
143 auto node = itsXML.allocate_node( rapidxml::node_declaration );
144 node->append_attribute( itsXML.allocate_attribute(
"version",
"1.0" ) );
145 node->append_attribute( itsXML.allocate_attribute(
"encoding",
"utf-8" ) );
146 itsXML.append_node( node );
149 auto root = itsXML.allocate_node( rapidxml::node_element, xml_detail::CEREAL_XML_STRING );
150 itsXML.append_node( root );
151 itsNodes.emplace( root );
154 itsStream << std::boolalpha;
155 itsStream.precision( options.itsPrecision );
156 itsOS << std::boolalpha;
157 itsOS.precision( options.itsPrecision );
163 const int flags = itsIndent ? 0x0 : rapidxml::print_no_indenting;
164 rapidxml::print( itsStream, itsXML, flags );
174 itsNodes.top().name = name;
178 auto base64string = base64::encode( reinterpret_cast<const unsigned char *>( data ), size );
179 saveValue( base64string );
182 itsNodes.top().node->append_attribute( itsXML.allocate_attribute(
"type",
"cereal binary data" ) );
202 const auto nameString = itsNodes.top().getValueName();
205 auto namePtr = itsXML.allocate_string( nameString.data(), nameString.length() + 1 );
208 auto node = itsXML.allocate_node( rapidxml::node_element, namePtr,
nullptr, nameString.size() );
209 itsNodes.top().node->append_node( node );
210 itsNodes.emplace( node );
222 itsNodes.top().name = name;
229 template <
class T>
inline 232 itsOS.clear(); itsOS.seekp( 0, std::ios::beg );
233 itsOS << value << std::ends;
235 auto strValue = itsOS.str();
240 strValue.resize(std::strlen(strValue.c_str()));
243 const auto len = strValue.length();
244 if ( len > 0 && ( xml_detail::isWhitespace( strValue[0] ) || xml_detail::isWhitespace( strValue[len - 1] ) ) )
246 itsNodes.top().node->append_attribute( itsXML.allocate_attribute(
"xml:space",
"preserve" ) );
250 auto dataPtr = itsXML.allocate_string(strValue.c_str(), strValue.length() + 1 );
253 itsNodes.top().node->append_node( itsXML.allocate_node( rapidxml::node_data,
nullptr, dataPtr ) );
259 saveValue( static_cast<uint32_t>( value ) );
265 saveValue( static_cast<int32_t>( value ) );
269 template <
class T>
inline 276 const auto nameString = util::demangledName<T>();
279 auto namePtr = itsXML.allocate_string( nameString.data(), nameString.length() + 1 );
281 itsNodes.top().node->append_attribute( itsXML.allocate_attribute(
"type", namePtr ) );
287 auto namePtr = itsXML.allocate_string( name );
288 auto valuePtr = itsXML.allocate_string( value );
289 itsNodes.top().node->append_attribute( itsXML.allocate_attribute( namePtr, valuePtr ) );
296 NodeInfo( rapidxml::xml_node<> * n =
nullptr,
297 const char * nm =
nullptr ) :
320 return "value" + std::to_string( counter++ ) +
"\0";
327 std::ostream & itsStream;
328 rapidxml::xml_document<> itsXML;
329 std::stack<NodeInfo> itsNodes;
330 std::ostringstream itsOS;
388 itsData(
std::istreambuf_iterator<char>( stream ),
std::istreambuf_iterator<char>() )
392 itsData.push_back(
'\0');
393 itsXML.parse<rapidxml::parse_trim_whitespace | rapidxml::parse_no_data_nodes | rapidxml::parse_declaration_node>(
reinterpret_cast<char *
>( itsData.data() ) );
395 catch( rapidxml::parse_error
const & )
404 throw Exception(
"XML Parsing failed - likely due to invalid characters or invalid naming");
408 auto root = itsXML.first_node( xml_detail::CEREAL_XML_STRING );
409 if( root ==
nullptr )
410 throw Exception(
"Could not detect cereal root node - likely due to empty or invalid input");
412 itsNodes.emplace( root );
429 loadValue( encoded );
431 auto decoded = base64::decode( encoded );
433 if( size != decoded.size() )
434 throw Exception(
"Decoded binary data size does not match specified size");
436 std::memcpy( data, decoded.data(), decoded.size() );
459 auto next = itsNodes.top().child;
460 auto const expectedName = itsNodes.top().name;
465 if( expectedName && ( next ==
nullptr || std::strcmp( next->name(), expectedName ) != 0 ) )
467 next = itsNodes.top().search( expectedName );
469 if( next ==
nullptr )
470 throw Exception(
"XML Parsing failed - provided NVP (" + std::string(expectedName) +
") not found");
473 itsNodes.emplace( next );
483 itsNodes.top().advance();
486 itsNodes.top().name =
nullptr;
493 return itsNodes.top().getChildName();
499 itsNodes.top().name = name;
503 template <class T, traits::EnableIf<std::is_unsigned<T>::value,
504 std::is_same<T, bool>::value> = traits::sfinae>
inline 507 std::istringstream is( itsNodes.top().node->value() );
508 is.setf( std::ios::boolalpha );
513 template <class T, traits::EnableIf<std::is_integral<T>::value,
514 !std::is_same<T, bool>::value,
515 sizeof(T) ==
sizeof(
char)> = traits::sfinae>
inline 518 value = *
reinterpret_cast<T*
>( itsNodes.top().node->value() );
524 int32_t val; loadValue( val ); value =
static_cast<int8_t
>( val );
530 uint32_t val; loadValue( val ); value =
static_cast<uint8_t
>( val );
534 template <class T, traits::EnableIf<std::is_unsigned<T>::value,
535 !std::is_same<T, bool>::value,
536 !std::is_same<T, char>::value,
537 !std::is_same<T, unsigned char>::value,
538 sizeof(T) <
sizeof(
long long)> = traits::sfinae>
inline 539 void loadValue( T & value )
541 value =
static_cast<T
>( std::stoul( itsNodes.top().node->value() ) );
545 template <class T, traits::EnableIf<std::is_unsigned<T>::value,
546 !std::is_same<T, bool>::value,
547 sizeof(T) >=
sizeof(
long long)> = traits::sfinae>
inline 550 value =
static_cast<T
>( std::stoull( itsNodes.top().node->value() ) );
554 template <class T, traits::EnableIf<std::is_signed<T>::value,
555 !std::is_same<T, char>::value,
556 sizeof(T) <=
sizeof(
int)> = traits::sfinae>
inline 557 void loadValue( T & value )
559 value =
static_cast<T
>( std::stoi( itsNodes.top().node->value() ) );
563 template <class T, traits::EnableIf<std::is_signed<T>::value,
564 (
sizeof(T) >
sizeof(
int)),
565 sizeof(T) <=
sizeof(long)> = traits::sfinae>
inline 566 void loadValue( T & value )
568 value =
static_cast<T
>( std::stol( itsNodes.top().node->value() ) );
572 template <class T, traits::EnableIf<std::is_signed<T>::value,
573 (
sizeof(T) >
sizeof(
long)),
574 sizeof(T) <=
sizeof(
long long)> = traits::sfinae>
inline 575 void loadValue( T & value )
577 value =
static_cast<T
>( std::stoll( itsNodes.top().node->value() ) );
581 void loadValue(
float & value )
585 value = std::stof( itsNodes.top().node->value() );
587 catch( std::out_of_range
const & )
590 std::istringstream is( itsNodes.top().node->value() );
592 if( std::fpclassify( value ) != FP_SUBNORMAL )
598 void loadValue(
double & value )
602 value = std::stod( itsNodes.top().node->value() );
604 catch( std::out_of_range
const & )
607 std::istringstream is( itsNodes.top().node->value() );
609 if( std::fpclassify( value ) != FP_SUBNORMAL )
619 value = std::stold( itsNodes.top().node->value() );
621 catch( std::out_of_range
const & )
624 std::istringstream is( itsNodes.top().node->value() );
626 if( std::fpclassify( value ) != FP_SUBNORMAL )
632 template<
class CharT,
class Traits,
class Alloc>
inline 633 void loadValue( std::basic_string<CharT, Traits, Alloc> & str )
635 std::basic_istringstream<CharT, Traits> is( itsNodes.top().node->value() );
637 str.assign( std::istreambuf_iterator<CharT, Traits>( is ),
638 std::istreambuf_iterator<CharT, Traits>() );
642 template <
class T>
inline 645 value = getNumChildren( itsNodes.top().node );
653 node = node->first_node();
655 while( node !=
nullptr )
658 node = node->next_sibling();
669 NodeInfo( rapidxml::xml_node<> * n =
nullptr ) :
671 child( n->first_node() ),
683 child = child->next_sibling();
690 rapidxml::xml_node<> *
search(
const char * searchName )
695 const size_t name_size = rapidxml::internal::measure( searchName );
697 for(
auto new_child = node->first_node(); new_child !=
nullptr; new_child = new_child->next_sibling() )
699 if( rapidxml::internal::compare( new_child->name(), new_child->name_size(), searchName, name_size, true ) )
716 return child ? child->name() :
nullptr;
728 std::vector<char> itsData;
729 rapidxml::xml_document<> itsXML;
730 std::stack<NodeInfo> itsNodes;
740 template <
class T>
inline 745 template <
class T>
inline 752 template <
class T>
inline 757 template <
class T>
inline 764 template <
class T>
inline 770 template <
class T>
inline 776 template <
class T>
inline 780 template <
class T>
inline 790 template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, XMLOutputArchive>::value ||
799 template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, XMLInputArchive>::value ||
811 template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, XMLOutputArchive>::value ||
819 template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, XMLInputArchive>::value ||
831 template <
class T>
inline 839 template <
class T>
inline 848 template <
class T>
inline 853 template <
class T>
inline 861 template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae>
inline 868 template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae>
inline 876 template<
class CharT,
class Traits,
class Alloc>
inline 883 template<
class CharT,
class Traits,
class Alloc>
inline 897 #endif // CEREAL_ARCHIVES_XML_HPP_ void setNextName(const char *name)
Sets the name for the next node created with startNode.
Definition: xml.hpp:220
Options(int precision=std::numeric_limits< double >::max_digits10, bool indent=true, bool outputType=false)
Specify specific options for the XMLOutputArchive.
Definition: xml.hpp:117
#define CEREAL_SETUP_ARCHIVE_TRAITS(InputArchive, OutputArchive)
Sets up traits that relate an input archive to an output archive.
Definition: traits.hpp:169
bool isWhitespace(char c)
Returns true if the character is whitespace.
Definition: xml.hpp:61
A wrapper around size metadata.
Definition: helpers.hpp:273
void prologue(JSONOutputArchive &, NameValuePair< T > const &)
Prologue for NVPs for JSON archives.
Definition: json.hpp:733
static Options NoIndent()
Default options with no indentation.
Definition: xml.hpp:111
#define CEREAL_XML_STRING_VALUE
The default name for the root node in a cereal xml archive.
Definition: xml.hpp:54
Type traits only struct used to mark an archive as human readable (text based)
Definition: traits.hpp:1299
void saveValue(T const &value)
Saves some data, encoded as a string, into the current top level node.
Definition: xml.hpp:230
A struct that contains metadata about a node.
Definition: xml.hpp:294
void insertType()
Causes the type to be appended as an attribute to the most recently made node if output type is set t...
Definition: xml.hpp:270
std::string getValueName()
Gets the name for the next child node created from this node.
Definition: xml.hpp:311
void saveValue(int8_t const &value)
Overload for int8_t prevents them from being serialized as characters.
Definition: xml.hpp:263
rapidxml::xml_node * node
A pointer to this node.
Definition: xml.hpp:303
void saveBinaryValue(const void *data, size_t size, const char *name=nullptr)
Saves some binary data, encoded as a base64 string, with an optional name.
Definition: xml.hpp:172
size_t counter
The counter for naming child nodes.
Definition: xml.hpp:304
An output archive designed to save data to XML.
Definition: xml.hpp:96
Definition: traits.hpp:1044
Definition: access.hpp:40
#define CEREAL_REGISTER_ARCHIVE(Archive)
Registers a specific Archive type with cereal.
Definition: cereal.hpp:141
void startNode()
Creates a new node that is a child of the node at the top of the stack.
Definition: xml.hpp:199
Main cereal functionality.
For holding name value pairs.
Definition: helpers.hpp:137
#define CEREAL_LOAD_FUNCTION_NAME
The deserialization (load) function name to search for.
Definition: macros.hpp:85
#define CEREAL_NOEXCEPT
Defines the CEREAL_NOEXCEPT macro to use instead of noexcept.
Definition: macros.hpp:130
void finishNode()
Designates the most recently added node as finished.
Definition: xml.hpp:214
The base output archive class.
Definition: cereal.hpp:234
void appendAttribute(const char *name, const char *value)
Appends an attribute to the current top level node.
Definition: xml.hpp:285
#define CEREAL_SAVE_FUNCTION_NAME
The serialization (save) function name to search for.
Definition: macros.hpp:92
static Options Default()
Default options.
Definition: xml.hpp:108
const char * name
The name for the next child node.
Definition: xml.hpp:305
void epilogue(JSONOutputArchive &, NameValuePair< T > const &)
Epilogue for NVPs for JSON archives.
Definition: json.hpp:745
~XMLOutputArchive() CEREAL_NOEXCEPT
Destructor, flushes the XML.
Definition: xml.hpp:161
A class containing various advanced options for the XML archive.
Definition: xml.hpp:104
An exception class thrown when things go wrong at runtime.
Definition: helpers.hpp:48
void saveValue(uint8_t const &value)
Overload for uint8_t prevents them from being serialized as characters.
Definition: xml.hpp:257
XMLOutputArchive(std::ostream &stream, Options const &options=Options::Default())
Construct, outputting to the provided stream upon destruction.
Definition: xml.hpp:136