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"
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 bool sizeAttributes_ =
true ) :
121 itsPrecision( precision_ ),
122 itsIndent( indent_ ),
123 itsOutputType( outputType_ ),
124 itsSizeAttributes( sizeAttributes_ )
155 bool itsSizeAttributes;
166 itsOutputType( options.itsOutputType ),
167 itsIndent( options.itsIndent ),
168 itsSizeAttributes(options.itsSizeAttributes)
171 auto node = itsXML.allocate_node( rapidxml::node_declaration );
172 node->append_attribute( itsXML.allocate_attribute(
"version",
"1.0" ) );
173 node->append_attribute( itsXML.allocate_attribute(
"encoding",
"utf-8" ) );
174 itsXML.append_node( node );
177 auto root = itsXML.allocate_node( rapidxml::node_element, xml_detail::CEREAL_XML_STRING );
178 itsXML.append_node( root );
179 itsNodes.emplace( root );
182 itsStream << std::boolalpha;
183 itsStream.precision( options.itsPrecision );
184 itsOS << std::boolalpha;
185 itsOS.precision( options.itsPrecision );
191 const int flags = itsIndent ? 0x0 : rapidxml::print_no_indenting;
192 rapidxml::print( itsStream, itsXML, flags );
202 itsNodes.top().name = name;
206 auto base64string = base64::encode(
reinterpret_cast<const unsigned char *
>( data ), size );
210 itsNodes.top().node->append_attribute( itsXML.allocate_attribute(
"type",
"cereal binary data" ) );
230 const auto nameString = itsNodes.top().getValueName();
233 auto namePtr = itsXML.allocate_string( nameString.data(), nameString.length() + 1 );
236 auto node = itsXML.allocate_node( rapidxml::node_element, namePtr,
nullptr, nameString.size() );
237 itsNodes.top().node->append_node( node );
238 itsNodes.emplace( node );
250 itsNodes.top().name = name;
257 template <
class T>
inline
260 itsOS.clear(); itsOS.seekp( 0, std::ios::beg );
261 itsOS << value << std::ends;
263 auto strValue = itsOS.str();
268 strValue.resize(std::strlen(strValue.c_str()));
271 const auto len = strValue.length();
272 if ( len > 0 && ( xml_detail::isWhitespace( strValue[0] ) || xml_detail::isWhitespace( strValue[len - 1] ) ) )
274 itsNodes.top().node->append_attribute( itsXML.allocate_attribute(
"xml:space",
"preserve" ) );
278 auto dataPtr = itsXML.allocate_string(strValue.c_str(), strValue.length() + 1 );
281 itsNodes.top().node->append_node( itsXML.allocate_node( rapidxml::node_data,
nullptr, dataPtr ) );
287 saveValue(
static_cast<uint32_t
>( value ) );
293 saveValue(
static_cast<int32_t
>( value ) );
297 template <
class T>
inline
304 const auto nameString = util::demangledName<T>();
307 auto namePtr = itsXML.allocate_string( nameString.data(), nameString.length() + 1 );
309 itsNodes.top().node->append_attribute( itsXML.allocate_attribute(
"type", namePtr ) );
315 auto namePtr = itsXML.allocate_string( name );
316 auto valuePtr = itsXML.allocate_string( value );
317 itsNodes.top().node->append_attribute( itsXML.allocate_attribute( namePtr, valuePtr ) );
320 bool hasSizeAttributes()
const {
return itsSizeAttributes; }
326 NodeInfo( rapidxml::xml_node<> * n =
nullptr,
327 const char * nm =
nullptr ) :
350 return "value" + std::to_string(
counter++ ) +
"\0";
357 std::ostream & itsStream;
358 rapidxml::xml_document<> itsXML;
359 std::stack<NodeInfo> itsNodes;
360 std::ostringstream itsOS;
363 bool itsSizeAttributes;
419 itsData( std::istreambuf_iterator<char>( stream ), std::istreambuf_iterator<char>() )
423 itsData.push_back(
'\0');
424 itsXML.parse<rapidxml::parse_trim_whitespace | rapidxml::parse_no_data_nodes | rapidxml::parse_declaration_node>(
reinterpret_cast<char *
>( itsData.data() ) );
426 catch( rapidxml::parse_error
const & )
435 throw Exception(
"XML Parsing failed - likely due to invalid characters or invalid naming");
439 auto root = itsXML.first_node( xml_detail::CEREAL_XML_STRING );
440 if( root ==
nullptr )
441 throw Exception(
"Could not detect cereal root node - likely due to empty or invalid input");
443 itsNodes.emplace( root );
462 auto decoded = base64::decode( encoded );
464 if( size != decoded.size() )
465 throw Exception(
"Decoded binary data size does not match specified size");
467 std::memcpy( data, decoded.data(), decoded.size() );
490 auto next = itsNodes.top().child;
491 auto const expectedName = itsNodes.top().name;
496 if( expectedName && ( next ==
nullptr || std::strcmp( next->name(), expectedName ) != 0 ) )
498 next = itsNodes.top().search( expectedName );
500 if( next ==
nullptr )
501 throw Exception(
"XML Parsing failed - provided NVP (" + std::string(expectedName) +
") not found");
504 itsNodes.emplace( next );
514 itsNodes.top().advance();
517 itsNodes.top().name =
nullptr;
524 return itsNodes.top().getChildName();
530 itsNodes.top().name = name;
534 template <class T, traits::EnableIf<std::is_unsigned<T>::value,
535 std::is_same<T, bool>::value> = traits::sfinae>
inline
538 std::istringstream is( itsNodes.top().node->value() );
539 is.setf( std::ios::boolalpha );
544 template <class T, traits::EnableIf<std::is_integral<T>::value,
545 !std::is_same<T, bool>::value,
546 sizeof(T) ==
sizeof(
char)> = traits::sfinae>
inline
549 value = *
reinterpret_cast<T*
>( itsNodes.top().node->value() );
555 int32_t val;
loadValue( val );
value =
static_cast<int8_t
>( val );
561 uint32_t val;
loadValue( val );
value =
static_cast<uint8_t
>( val );
565 template <class T, traits::EnableIf<std::is_unsigned<T>::value,
566 !std::is_same<T, bool>::value,
567 !std::is_same<T, char>::value,
568 !std::is_same<T, unsigned char>::value,
569 sizeof(T) <
sizeof(
long long)> = traits::sfinae>
inline
572 value =
static_cast<T
>( std::stoul( itsNodes.top().node->value() ) );
576 template <class T, traits::EnableIf<std::is_unsigned<T>::value,
577 !std::is_same<T, bool>::value,
578 sizeof(T) >=
sizeof(
long long)> = traits::sfinae>
inline
581 value =
static_cast<T
>( std::stoull( itsNodes.top().node->value() ) );
585 template <class T, traits::EnableIf<std::is_signed<T>::value,
586 !std::is_same<T, char>::value,
587 sizeof(T) <=
sizeof(
int)> = traits::sfinae>
inline
590 value =
static_cast<T
>( std::stoi( itsNodes.top().node->value() ) );
594 template <class T, traits::EnableIf<std::is_signed<T>::value,
595 (
sizeof(T) >
sizeof(
int)),
596 sizeof(T) <=
sizeof(long)> = traits::sfinae>
inline
599 value =
static_cast<T
>( std::stol( itsNodes.top().node->value() ) );
603 template <class T, traits::EnableIf<std::is_signed<T>::value,
604 (
sizeof(T) >
sizeof(
long)),
605 sizeof(T) <=
sizeof(
long long)> = traits::sfinae>
inline
608 value =
static_cast<T
>( std::stoll( itsNodes.top().node->value() ) );
616 value = std::stof( itsNodes.top().node->value() );
618 catch( std::out_of_range
const & )
621 std::istringstream is( itsNodes.top().node->value() );
623 if( std::fpclassify(
value ) != FP_SUBNORMAL )
633 value = std::stod( itsNodes.top().node->value() );
635 catch( std::out_of_range
const & )
638 std::istringstream is( itsNodes.top().node->value() );
640 if( std::fpclassify(
value ) != FP_SUBNORMAL )
650 value = std::stold( itsNodes.top().node->value() );
652 catch( std::out_of_range
const & )
655 std::istringstream is( itsNodes.top().node->value() );
657 if( std::fpclassify(
value ) != FP_SUBNORMAL )
663 template<
class CharT,
class Traits,
class Alloc>
inline
664 void loadValue( std::basic_string<CharT, Traits, Alloc> & str )
666 std::basic_istringstream<CharT, Traits> is( itsNodes.top().node->value() );
668 str.assign( std::istreambuf_iterator<CharT, Traits>( is ),
669 std::istreambuf_iterator<CharT, Traits>() );
673 template <
class T>
inline
684 node = node->first_node();
686 while( node !=
nullptr )
689 node = node->next_sibling();
700 NodeInfo( rapidxml::xml_node<> * n =
nullptr ) :
702 child( n->first_node() ),
721 rapidxml::xml_node<> *
search(
const char * searchName )
726 const size_t name_size = rapidxml::internal::measure( searchName );
728 for(
auto new_child =
node->first_node(); new_child !=
nullptr; new_child = new_child->next_sibling() )
730 if( rapidxml::internal::compare( new_child->name(), new_child->name_size(), searchName, name_size,
true ) )
759 std::vector<char> itsData;
760 rapidxml::xml_document<> itsXML;
761 std::stack<NodeInfo> itsNodes;
771 template <
class T>
inline
776 template <
class T>
inline
783 template <
class T>
inline
788 template <
class T>
inline
795 template <
class T>
inline
800 template <
class T>
inline
807 template <
class T>
inline
813 template <
class T>
inline
820 template <
class T>
inline
823 if (ar.hasSizeAttributes())
829 template <
class T>
inline
830 void prologue( XMLInputArchive &, SizeTag<T>
const & )
835 template <
class T>
inline
839 template <
class T>
inline
840 void epilogue( XMLInputArchive &, SizeTag<T>
const & )
849 template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, XMLOutputArchive>::value ||
850 traits::has_minimal_output_serialization<T, XMLOutputArchive>::value> = traits::sfinae>
inline
858 template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, XMLInputArchive>::value ||
859 traits::has_minimal_input_serialization<T, XMLInputArchive>::value> = traits::sfinae>
inline
870 template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, XMLOutputArchive>::value ||
871 traits::has_minimal_output_serialization<T, XMLOutputArchive>::value> = traits::sfinae>
inline
878 template <class T, traits::DisableIf<traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, XMLInputArchive>::value ||
879 traits::has_minimal_input_serialization<T, XMLInputArchive>::value> = traits::sfinae>
inline
890 template <
class T>
inline
898 template <
class T>
inline
907 template <
class T>
inline
912 template <
class T>
inline
920 template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae>
inline
927 template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae>
inline
935 template<
class CharT,
class Traits,
class Alloc>
inline
942 template<
class CharT,
class Traits,
class Alloc>
inline
Main cereal functionality.
A wrapper around data that should be serialized after all non-deferred data.
Definition: helpers.hpp:233
For holding name value pairs.
Definition: helpers.hpp:140
The base output archive class.
Definition: cereal.hpp:319
A wrapper around size metadata.
Definition: helpers.hpp:313
A class containing various advanced options for the XML archive.
Definition: xml.hpp:107
Options & precision(int value)
Sets the precision used for floaing point numbers.
Definition: xml.hpp:140
Options & sizeAttributes(bool enable)
Whether dynamically sized containers (e.g. vector) output the size=dynamic attribute.
Definition: xml.hpp:146
Options & outputType(bool enable)
Whether to output the type of each serialized object as an attribute.
Definition: xml.hpp:144
static Options Default()
Default options.
Definition: xml.hpp:110
Options(int precision_=std::numeric_limits< double >::max_digits10, bool indent_=true, bool outputType_=false, bool sizeAttributes_=true)
Specify specific options for the XMLOutputArchive.
Definition: xml.hpp:117
Options & indent(bool enable)
Whether to indent each line of XML.
Definition: xml.hpp:142
An output archive designed to save data to XML.
Definition: xml.hpp:97
void saveValue(int8_t const &value)
Overload for int8_t prevents them from being serialized as characters.
Definition: xml.hpp:291
~XMLOutputArchive() CEREAL_NOEXCEPT
Destructor, flushes the XML.
Definition: xml.hpp:189
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:200
void saveValue(uint8_t const &value)
Overload for uint8_t prevents them from being serialized as characters.
Definition: xml.hpp:285
XMLOutputArchive(std::ostream &stream, Options const &options=Options::Default())
Construct, outputting to the provided stream upon destruction.
Definition: xml.hpp:163
void startNode()
Creates a new node that is a child of the node at the top of the stack.
Definition: xml.hpp:227
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:298
void saveValue(T const &value)
Saves some data, encoded as a string, into the current top level node.
Definition: xml.hpp:258
void setNextName(const char *name)
Sets the name for the next node created with startNode.
Definition: xml.hpp:248
void finishNode()
Designates the most recently added node as finished.
Definition: xml.hpp:242
void appendAttribute(const char *name, const char *value)
Appends an attribute to the current top level node.
Definition: xml.hpp:313
#define CEREAL_REGISTER_ARCHIVE(Archive)
Registers a specific Archive type with cereal.
Definition: cereal.hpp:195
void epilogue(JSONOutputArchive &, NameValuePair< T > const &)
Epilogue for NVPs for JSON archives.
Definition: json.hpp:763
void prologue(JSONOutputArchive &, NameValuePair< T > const &)
Prologue for NVPs for JSON archives.
Definition: json.hpp:751
#define CEREAL_NOEXCEPT
Defines the CEREAL_NOEXCEPT macro to use instead of noexcept.
Definition: macros.hpp:130
#define CEREAL_LOAD_FUNCTION_NAME
The deserialization (load) function name to search for.
Definition: macros.hpp:85
#define CEREAL_SAVE_FUNCTION_NAME
The serialization (save) function name to search for.
Definition: macros.hpp:92
An exception class thrown when things go wrong at runtime.
Definition: helpers.hpp:49
A struct that contains metadata about a node.
Definition: xml.hpp:325
rapidxml::xml_node * node
A pointer to this node.
Definition: xml.hpp:333
std::string getValueName()
Gets the name for the next child node created from this node.
Definition: xml.hpp:341
const char * name
The name for the next child node.
Definition: xml.hpp:335
size_t counter
The counter for naming child nodes.
Definition: xml.hpp:334
Type traits only struct used to mark an archive as human readable (text based)
Definition: traits.hpp:1321
#define CEREAL_SETUP_ARCHIVE_TRAITS(InputArchive, OutputArchive)
Sets up traits that relate an input archive to an output archive.
Definition: traits.hpp:169
#define CEREAL_XML_STRING_VALUE
The default name for the root node in a cereal xml archive.
Definition: xml.hpp:54
bool isWhitespace(char c)
Returns true if the character is whitespace.
Definition: xml.hpp:61