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 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
956 #endif // CEREAL_ARCHIVES_XML_HPP_