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 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 );
421 loadValue( encoded );
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, char>::value,
529 !std::is_same<T, unsigned char>::value,
530 sizeof(T) <
sizeof(
long long)> = traits::sfinae>
inline 531 void loadValue( T & value )
533 value =
static_cast<T
>( std::stoul( itsNodes.top().node->value() ) );
537 template <class T, traits::EnableIf<std::is_unsigned<T>::value,
538 !std::is_same<T, bool>::value,
539 sizeof(T) >=
sizeof(
long long)> = traits::sfinae>
inline 542 value =
static_cast<T
>( std::stoull( itsNodes.top().node->value() ) );
546 template <class T, traits::EnableIf<std::is_signed<T>::value,
547 !std::is_same<T, char>::value,
548 sizeof(T) <=
sizeof(
int)> = traits::sfinae>
inline 549 void loadValue( T & value )
551 value =
static_cast<T
>( std::stoi( itsNodes.top().node->value() ) );
555 template <class T, traits::EnableIf<std::is_signed<T>::value,
556 (
sizeof(T) >
sizeof(
int)),
557 sizeof(T) <=
sizeof(long)> = traits::sfinae>
inline 558 void loadValue( T & value )
560 value =
static_cast<T
>( std::stol( itsNodes.top().node->value() ) );
564 template <class T, traits::EnableIf<std::is_signed<T>::value,
565 (
sizeof(T) >
sizeof(
long)),
566 sizeof(T) <=
sizeof(
long long)> = traits::sfinae>
inline 567 void loadValue( T & value )
569 value =
static_cast<T
>( std::stoll( itsNodes.top().node->value() ) );
573 void loadValue(
float & value )
577 value = std::stof( itsNodes.top().node->value() );
579 catch( std::out_of_range
const & )
582 std::istringstream is( itsNodes.top().node->value() );
584 if( std::fpclassify( value ) != FP_SUBNORMAL )
590 void loadValue(
double & value )
594 value = std::stod( itsNodes.top().node->value() );
596 catch( std::out_of_range
const & )
599 std::istringstream is( itsNodes.top().node->value() );
601 if( std::fpclassify( value ) != FP_SUBNORMAL )
611 value = std::stold( itsNodes.top().node->value() );
613 catch( std::out_of_range
const & )
616 std::istringstream is( itsNodes.top().node->value() );
618 if( std::fpclassify( value ) != FP_SUBNORMAL )
624 template<
class CharT,
class Traits,
class Alloc>
inline 625 void loadValue( std::basic_string<CharT, Traits, Alloc> & str )
627 std::basic_istringstream<CharT, Traits> is( itsNodes.top().node->value() );
629 str.assign( std::istreambuf_iterator<CharT, Traits>( is ),
630 std::istreambuf_iterator<CharT, Traits>() );
634 template <
class T>
inline 637 value = getNumChildren( itsNodes.top().node );
645 node = node->first_node();
647 while( node !=
nullptr )
650 node = node->next_sibling();
661 NodeInfo( rapidxml::xml_node<> * n =
nullptr ) :
663 child( n->first_node() ),
675 child = child->next_sibling();
682 rapidxml::xml_node<> *
search(
const char * searchName )
687 const size_t name_size = rapidxml::internal::measure( searchName );
689 for(
auto new_child = node->first_node(); new_child !=
nullptr; new_child = new_child->next_sibling() )
691 if( rapidxml::internal::compare( new_child->name(), new_child->name_size(), searchName, name_size, true ) )
714 std::vector<char> itsData;
715 rapidxml::xml_document<> itsXML;
716 std::stack<NodeInfo> itsNodes;
726 template <
class T>
inline 731 template <
class T>
inline 738 template <
class T>
inline 743 template <
class T>
inline 750 template <
class T>
inline 756 template <
class T>
inline 762 template <
class T>
inline 766 template <
class T>
inline 776 template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, XMLOutputArchive>::value ||
785 template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, XMLInputArchive>::value ||
797 template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, XMLOutputArchive>::value ||
805 template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, XMLInputArchive>::value ||
817 template <
class T>
inline 825 template <
class T>
inline 834 template <
class T>
inline 839 template <
class T>
inline 847 template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae>
inline 854 template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae>
inline 862 template<
class CharT,
class Traits,
class Alloc>
inline 869 template<
class CharT,
class Traits,
class Alloc>
inline 883 #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
bool isWhitespace(char c)
Returns true if the character is whitespace.
Definition: xml.hpp:61
A wrapper around size metadata.
Definition: helpers.hpp:250
void prologue(JSONOutputArchive &, NameValuePair< T > const &)
Prologue for NVPs for JSON archives.
Definition: json.hpp:702
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: traits.hpp:990
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
void epilogue(JSONOutputArchive &, NameValuePair< T > const &)
Epilogue for NVPs for JSON archives.
Definition: json.hpp:714
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