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
61 inline bool isWhitespace(
char c )
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 );
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 const auto strValue = itsOS.str();
240 const auto len = strValue.length();
241 if ( len > 1 && ( xml_detail::isWhitespace( strValue[0] ) || xml_detail::isWhitespace( strValue[len - 2] ) ) )
243 itsNodes.top().node->append_attribute( itsXML.allocate_attribute(
"xml:space",
"preserve" ) );
247 auto dataPtr = itsXML.allocate_string( itsOS.str().c_str(), itsOS.str().length() + 1 );
250 itsNodes.top().node->append_node( itsXML.allocate_node( rapidxml::node_data,
nullptr, dataPtr ) );
256 saveValue( static_cast<uint32_t>( value ) );
262 saveValue( static_cast<int32_t>( value ) );
266 template <
class T>
inline
273 const auto nameString = util::demangledName<T>();
276 auto namePtr = itsXML.allocate_string( nameString.data(), nameString.length() + 1 );
278 itsNodes.top().node->append_attribute( itsXML.allocate_attribute(
"type", namePtr ) );
284 auto namePtr = itsXML.allocate_string( name );
285 auto valuePtr = itsXML.allocate_string( value );
286 itsNodes.top().node->append_attribute( itsXML.allocate_attribute( namePtr, valuePtr ) );
293 NodeInfo( rapidxml::xml_node<> * n =
nullptr,
294 const char * nm =
nullptr ) :
317 return "value" + std::to_string( counter++ ) +
"\0";
324 std::ostream & itsStream;
325 rapidxml::xml_document<> itsXML;
326 std::stack<NodeInfo> itsNodes;
327 std::ostringstream itsOS;
382 itsData(
std::istreambuf_iterator<char>( stream ),
std::istreambuf_iterator<char>() )
386 itsData.push_back(
'\0');
387 itsXML.parse<rapidxml::parse_trim_whitespace | rapidxml::parse_no_data_nodes | rapidxml::parse_declaration_node>(
reinterpret_cast<char *
>( itsData.data() ) );
389 catch( rapidxml::parse_error
const & )
398 throw Exception(
"XML Parsing failed - likely due to invalid characters or invalid naming");
402 auto root = itsXML.first_node( xml_detail::CEREAL_XML_STRING );
403 if( root ==
nullptr )
404 throw Exception(
"Could not detect cereal root node - likely due to empty or invalid input");
406 itsNodes.emplace( root );
423 auto decoded = base64::decode( encoded );
425 if( size != decoded.size() )
426 throw Exception(
"Decoded binary data size does not match specified size");
428 std::memcpy( data, decoded.data(), decoded.size() );
451 auto next = itsNodes.top().child;
452 auto const expectedName = itsNodes.top().name;
457 if( expectedName && ( next ==
nullptr || std::strcmp( next->name(), expectedName ) != 0 ) )
459 next = itsNodes.top().search( expectedName );
461 if( next ==
nullptr )
462 throw Exception(
"XML Parsing failed - provided NVP not found");
465 itsNodes.emplace( next );
475 itsNodes.top().advance();
478 itsNodes.top().name =
nullptr;
485 return itsNodes.top().node->name();
491 itsNodes.top().name = name;
495 template <class T, traits::EnableIf<std::is_unsigned<T>::value,
496 std::is_same<T, bool>::value> = traits::sfinae>
inline
499 std::istringstream is( itsNodes.top().node->value() );
500 is.setf( std::ios::boolalpha );
505 template <class T, traits::EnableIf<std::is_integral<T>::value,
506 !std::is_same<T, bool>::value,
507 sizeof(T) ==
sizeof(
char)> = traits::sfinae>
inline
510 value = *
reinterpret_cast<T*
>( itsNodes.top().node->value() );
516 int32_t val;
loadValue( val ); value =
static_cast<int8_t
>( val );
522 uint32_t val;
loadValue( val ); value =
static_cast<uint8_t
>( val );
526 template <class T, traits::EnableIf<std::is_unsigned<T>::value,
527 !std::is_same<T, bool>::value,
528 !std::is_same<T, unsigned char>::value,
529 sizeof(T) <
sizeof(
long long)> = traits::sfinae>
inline
532 value =
static_cast<T
>( std::stoul( itsNodes.top().node->value() ) );
536 template <class T, traits::EnableIf<std::is_unsigned<T>::value,
537 !std::is_same<T, bool>::value,
538 sizeof(T) >=
sizeof(
long long)> = traits::sfinae>
inline
541 value =
static_cast<T
>( std::stoull( itsNodes.top().node->value() ) );
545 template <class T, traits::EnableIf<std::is_signed<T>::value,
546 !std::is_same<T, char>::value,
547 sizeof(T) <=
sizeof(
int)> = traits::sfinae>
inline
550 value =
static_cast<T
>( std::stoi( itsNodes.top().node->value() ) );
554 template <class T, traits::EnableIf<std::is_signed<T>::value,
555 (
sizeof(T) >
sizeof(
int)),
556 sizeof(T) <=
sizeof(long)> = traits::sfinae>
inline
559 value =
static_cast<T
>( std::stol( itsNodes.top().node->value() ) );
563 template <class T, traits::EnableIf<std::is_signed<T>::value,
564 (
sizeof(T) >
sizeof(
long)),
565 sizeof(T) <=
sizeof(
long long)> = traits::sfinae>
inline
568 value =
static_cast<T
>( std::stoll( itsNodes.top().node->value() ) );
576 value = std::stof( itsNodes.top().node->value() );
578 catch( std::out_of_range
const & )
581 std::istringstream is( itsNodes.top().node->value() );
583 if( std::fpclassify( value ) != FP_SUBNORMAL )
593 value = std::stod( itsNodes.top().node->value() );
595 catch( std::out_of_range
const & )
598 std::istringstream is( itsNodes.top().node->value() );
600 if( std::fpclassify( value ) != FP_SUBNORMAL )
610 value = std::stold( itsNodes.top().node->value() );
612 catch( std::out_of_range
const & )
615 std::istringstream is( itsNodes.top().node->value() );
617 if( std::fpclassify( value ) != FP_SUBNORMAL )
623 template<
class CharT,
class Traits,
class Alloc>
inline
624 void loadValue( std::basic_string<CharT, Traits, Alloc> & str )
626 std::basic_istringstream<CharT, Traits> is( itsNodes.top().node->value() );
628 str.assign( std::istreambuf_iterator<CharT, Traits>( is ),
629 std::istreambuf_iterator<CharT, Traits>() );
633 template <
class T>
inline
644 node = node->first_node();
646 while( node !=
nullptr )
649 node = node->next_sibling();
660 NodeInfo( rapidxml::xml_node<> * n =
nullptr ) :
662 child( n->first_node() ),
681 rapidxml::xml_node<> *
search(
const char * searchName )
686 const size_t name_size = rapidxml::internal::measure( searchName );
688 for(
auto new_child =
node->first_node(); new_child !=
nullptr; new_child = new_child->next_sibling() )
690 if( rapidxml::internal::compare( new_child->name(), new_child->name_size(), searchName, name_size, true ) )
713 std::vector<char> itsData;
714 rapidxml::xml_document<> itsXML;
715 std::stack<NodeInfo> itsNodes;
725 template <
class T>
inline
730 template <
class T>
inline
731 void prologue( XMLInputArchive &, NameValuePair<T>
const & )
737 template <
class T>
inline
738 void epilogue( XMLOutputArchive &, NameValuePair<T>
const & )
742 template <
class T>
inline
743 void epilogue( XMLInputArchive &, NameValuePair<T>
const & )
749 template <
class T>
inline
750 void prologue( XMLOutputArchive & ar, SizeTag<T>
const & )
752 ar.appendAttribute(
"size",
"dynamic" );
755 template <
class T>
inline
756 void prologue( XMLInputArchive &, SizeTag<T>
const & )
761 template <
class T>
inline
762 void epilogue( XMLOutputArchive &, SizeTag<T>
const & )
765 template <
class T>
inline
766 void epilogue( XMLInputArchive &, SizeTag<T>
const & )
775 template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, XMLOutputArchive>::value ||
776 traits::has_minimal_output_serialization<T, XMLOutputArchive>::value> = traits::sfinae>
inline
777 void prologue( XMLOutputArchive & ar, T
const & )
784 template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, XMLInputArchive>::value ||
785 traits::has_minimal_input_serialization<T, XMLInputArchive>::value> = traits::sfinae>
inline
786 void prologue( XMLInputArchive & ar, T
const & )
796 template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, XMLOutputArchive>::value ||
797 traits::has_minimal_output_serialization<T, XMLOutputArchive>::value> = traits::sfinae>
inline
798 void epilogue( XMLOutputArchive & ar, T
const & )
804 template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, XMLInputArchive>::value ||
805 traits::has_minimal_input_serialization<T, XMLInputArchive>::value> = traits::sfinae>
inline
806 void epilogue( XMLInputArchive & ar, T
const & )
816 template <
class T>
inline
819 ar.setNextName( t.name );
824 template <
class T>
inline
827 ar.setNextName( t.name );
833 template <
class T>
inline
838 template <
class T>
inline
841 ar.loadSize( st.size );
846 template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae>
inline
853 template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae>
inline
861 template<
class CharT,
class Traits,
class Alloc>
inline
868 template<
class CharT,
class Traits,
class Alloc>
inline
882 #endif // CEREAL_ARCHIVES_XML_HPP_
void setNextName(const char *name)
Sets the name for the next node created with startNode.
Definition: xml.hpp:220
~XMLOutputArchive()
Destructor, flushes the XML.
Definition: xml.hpp:161
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
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:1266
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:291
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:267
std::string getValueName()
Gets the name for the next child node created from this node.
Definition: xml.hpp:308
void saveValue(int8_t const &value)
Overload for int8_t prevents them from being serialized as characters.
Definition: xml.hpp:260
rapidxml::xml_node * node
A pointer to this node.
Definition: xml.hpp:300
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:301
An output archive designed to save data to XML.
Definition: xml.hpp:96
Definition: access.hpp:39
#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:135
#define CEREAL_LOAD_FUNCTION_NAME
The deserialization (load) function name to search for.
Definition: macros.hpp:58
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:282
#define CEREAL_SAVE_FUNCTION_NAME
The serialization (save) function name to search for.
Definition: macros.hpp:65
static Options Default()
Default options.
Definition: xml.hpp:108
const char * name
The name for the next child node.
Definition: xml.hpp:302
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:254
XMLOutputArchive(std::ostream &stream, Options const &options=Options::Default())
Construct, outputting to the provided stream upon destruction.
Definition: xml.hpp:136