cereal
A C++11 library for serialization
 All Classes Files Functions Variables Typedefs Enumerations Groups
xml.hpp
Go to the documentation of this file.
1 
3 /*
4  Copyright (c) 2013, Randolph Voorhies, Shane Grant
5  All rights reserved.
6 
7  Redistribution and use in source and binary forms, with or without
8  modification, are permitted provided that the following conditions are met:
9  * Redistributions of source code must retain the above copyright
10  notice, this list of conditions and the following disclaimer.
11  * Redistributions in binary form must reproduce the above copyright
12  notice, this list of conditions and the following disclaimer in the
13  documentation and/or other materials provided with the distribution.
14  * Neither the name of cereal nor the
15  names of its contributors may be used to endorse or promote products
16  derived from this software without specific prior written permission.
17 
18  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
22  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29 #ifndef CEREAL_ARCHIVES_XML_HPP_
30 #define CEREAL_ARCHIVES_XML_HPP_
31 
32 #include <cereal/cereal.hpp>
33 #include <cereal/details/util.hpp>
34 
37 #include <cereal/external/base64.hpp>
38 
39 #include <sstream>
40 #include <stack>
41 #include <vector>
42 #include <string>
43 #include <cstring>
44 
45 namespace cereal
46 {
47  namespace xml_detail
48  {
49  static const char * CEREAL_XML_STRING = "cereal";
50  }
51 
52  // ######################################################################
54 
55  class XMLOutputArchive : public OutputArchive<XMLOutputArchive>
56  {
57  public:
59 
63  XMLOutputArchive(std::ostream & stream, size_t precision = 10, bool outputType = false ) :
65  itsStream(stream),
66  itsOutputType( outputType )
67  {
68  // rapidxml will delete all allocations when xml_document is cleared
69  auto node = itsXML.allocate_node( rapidxml::node_declaration );
70  node->append_attribute( itsXML.allocate_attribute( "version", "1.0" ) );
71  node->append_attribute( itsXML.allocate_attribute( "encoding", "utf-8" ) );
72  itsXML.append_node( node );
73 
74  // allocate root node
75  auto root = itsXML.allocate_node( rapidxml::node_element, xml_detail::CEREAL_XML_STRING );
76  itsXML.append_node( root );
77  itsNodes.emplace( root );
78 
79  // set attributes on the streams
80  itsStream << std::boolalpha;
81  itsStream.precision( precision );
82  itsOS << std::boolalpha;
83  itsOS.precision( precision );
84  }
85 
88  {
89  itsStream << itsXML;
90  itsXML.clear();
91  }
92 
94 
98  void startNode()
99  {
100  // generate a name for this new node
101  const auto nameString = itsNodes.top().getValueName();
102 
103  // allocate strings for all of the data in the XML object
104  auto namePtr = itsXML.allocate_string( nameString.data(), nameString.size() );
105 
106  // insert into the XML
107  auto node = itsXML.allocate_node( rapidxml::node_element, namePtr, nullptr, nameString.size() );
108  itsNodes.top().node->append_node( node );
109  itsNodes.emplace( node );
110  }
111 
113  void finishNode()
114  {
115  itsNodes.pop();
116  }
117 
119  void setNextName( const char * name )
120  {
121  itsNodes.top().name = name;
122  }
123 
125 
128  template <class T> inline
129  void saveValue( T const & value )
130  {
131  itsOS.clear(); itsOS.seekp(0);
132  itsOS << value << std::ends;
133 
134  // allocate strings for all of the data in the XML object
135  auto dataPtr = itsXML.allocate_string( itsOS.str().c_str() );
136 
137  // insert into the XML
138  itsNodes.top().node->append_node( itsXML.allocate_node( rapidxml::node_data, nullptr, dataPtr ) );
139  }
140 
142  void saveValue( uint8_t const & value )
143  {
144  saveValue( static_cast<uint32_t>( value ) );
145  }
146 
148 
150  void saveBinaryValue( const void * data, size_t size, const char * name = nullptr )
151  {
152  itsNodes.top().name = name;
153 
154  startNode();
155 
156  auto base64string = base64::encode( reinterpret_cast<const unsigned char *>( data ), size );
157  saveValue( base64string );
158 
159  if( itsOutputType )
160  itsNodes.top().node->append_attribute( itsXML.allocate_attribute( "type", "cereal binary data" ) );
161 
162  finishNode();
163  };
164 
166  template <class T> inline
167  void insertType()
168  {
169  if( !itsOutputType )
170  return;
171 
172  // generate a name for this new node
173  const auto nameString = util::demangledName<T>();
174 
175  // allocate strings for all of the data in the XML object
176  auto namePtr = itsXML.allocate_string( nameString.data(), nameString.size() );
177 
178  itsNodes.top().node->append_attribute( itsXML.allocate_attribute( "type", namePtr ) );
179  }
180 
181  protected:
183  struct NodeInfo
184  {
185  NodeInfo( rapidxml::xml_node<> * n = nullptr,
186  const char * nm = nullptr ) :
187  node( n ),
188  counter( 0 ),
189  name( nm )
190  { }
191 
193  size_t counter;
194  const char * name;
195 
197 
200  std::string getValueName()
201  {
202  if( name )
203  {
204  auto n = name;
205  name = nullptr;
206  return {n};
207  }
208  else
209  return "value" + std::to_string( counter++ ) + "\0";
210  }
211  }; // NodeInfo
212 
213  private:
214  std::ostream & itsStream;
215  rapidxml::xml_document<> itsXML;
216  std::stack<NodeInfo> itsNodes;
217  std::ostringstream itsOS;
218  bool itsOutputType;
219  }; // XMLOutputArchive
220 
221  // ######################################################################
223 
224  class XMLInputArchive : public InputArchive<XMLInputArchive>
225  {
226  public:
228 
232  XMLInputArchive( std::istream & stream ) :
233  InputArchive<XMLInputArchive>( this ),
234  itsData( std::istreambuf_iterator<char>( stream ), std::istreambuf_iterator<char>() )
235  {
236  try
237  {
238  itsXML.parse<rapidxml::parse_no_data_nodes | rapidxml::parse_declaration_node>( reinterpret_cast<char *>( itsData.data() ) );
239  }
240  catch( rapidxml::parse_error const & e )
241  {
242  throw Exception("XML Parsing failed - likely due to invalid characters or invalid naming");
243  //std::cout << e.what() << std::endl;
244  //std::cout << e.where<char>() << std::endl;
245  }
246 
247  // Parse the root
248  auto root = itsXML.first_node( xml_detail::CEREAL_XML_STRING );
249  itsNodes.emplace( root );
250  }
251 
253  void startNode()
254  {
255  itsNodes.emplace( itsNodes.top().child );
256  }
257 
259  void finishNode()
260  {
261  // remove current
262  itsNodes.pop();
263 
264  // advance parent
265  itsNodes.top().advance();
266  }
267 
269  template <class T> inline
270  void loadValue( T & value )
271  {
272  std::istringstream is( itsNodes.top().node->value() );
273  is.setf( std::ios::boolalpha );
274  is >> value;
275  }
276 
278  template<class CharT, class Traits, class Alloc> inline
279  void loadValue( std::basic_string<CharT, Traits, Alloc> & str )
280  {
281  std::basic_istringstream<CharT, Traits> is( itsNodes.top().node->value() );
282 
283  str.assign( std::istreambuf_iterator<CharT, Traits>( is ),
284  std::istreambuf_iterator<CharT, Traits>() );
285  }
286 
288 
289  void loadBinaryValue( void * data, size_t size )
290  {
291  startNode();
292 
293  std::string encoded;
294  loadValue( encoded );
295 
296  auto decoded = base64::decode( encoded );
297 
298  if( size != decoded.size() )
299  throw Exception("Decoded binary data size does not match specified size");
300 
301  std::memcpy( data, decoded.data(), decoded.size() );
302 
303  finishNode();
304  };
305 
307  template <class T> inline
308  void loadSize( T & value )
309  {
310  value = getNumChildren( itsNodes.top().node );
311  }
312 
314  static size_t getNumChildren( rapidxml::xml_node<> * node = nullptr )
315  {
316  size_t size = 0;
317  node = node->first_node(); // get first child
318 
319  while( node != nullptr )
320  {
321  ++size;
322  node = node->next_sibling();
323  }
324 
325  return size;
326  }
327 
328  protected:
330  struct NodeInfo
331  {
332  NodeInfo( rapidxml::xml_node<> * n = nullptr ) :
333  node( n ),
334  child( n ? n->first_node() : nullptr ),
336  { }
337 
338  void advance()
339  {
340  if( size )
341  {
342  --size;
343  child = child->next_sibling();
344  }
345  }
346 
349  size_t size;
350  }; // NodeInfo
351 
352  private:
353  std::vector<uint8_t> itsData;
354  rapidxml::xml_document<> itsXML;
355  std::stack<NodeInfo> itsNodes;
356  };
357 
358  // ######################################################################
359  // XMLArchive prologue and epilogue functions
360  // ######################################################################
361 
362  // ######################################################################
364 
365  template <class T>
366  void prologue( XMLOutputArchive &, NameValuePair<T> const & )
367  { }
368 
370  template <class T>
371  void prologue( XMLInputArchive &, NameValuePair<T> const & )
372  { }
373 
374  // ######################################################################
376 
377  template <class T>
378  void epilogue( XMLOutputArchive &, NameValuePair<T> const & )
379  { }
380 
382  template <class T>
383  void epilogue( XMLInputArchive &, NameValuePair<T> const & )
384  { }
385 
386  // ######################################################################
388 
389  template <class T>
390  void prologue( XMLOutputArchive &, SizeTag<T> const & )
391  { }
392 
393  template <class T>
394  void prologue( XMLInputArchive &, SizeTag<T> const & )
395  { }
396 
398 
399  template <class T>
400  void epilogue( XMLOutputArchive &, SizeTag<T> const & )
401  { }
402 
403  template <class T>
404  void epilogue( XMLInputArchive &, SizeTag<T> const & )
405  { }
406 
407  // ######################################################################
409 
411  template <class T>
412  void prologue( XMLOutputArchive & ar, T const & )
413  {
414  ar.startNode();
415  ar.insertType<T>();
416  }
417 
419  template <class T>
420  void prologue( XMLInputArchive & ar, T const & )
421  {
422  ar.startNode();
423  }
424 
425  // ######################################################################
427 
428  template <class T>
429  void epilogue( XMLOutputArchive & ar, T const & )
430  {
431  ar.finishNode();
432  }
433 
435  template <class T>
436  void epilogue( XMLInputArchive & ar, T const & )
437  {
438  ar.finishNode();
439  }
440 
441  // ######################################################################
442  // Common XMLArchive serialization functions
443  // ######################################################################
444 
446  template <class T> inline
447  void save( XMLOutputArchive & ar, NameValuePair<T> const & t )
448  {
449  ar.setNextName( t.name );
450  ar( t.value );
451  }
452 
454  template <class T> inline
455  void load( XMLInputArchive & ar, NameValuePair<T> & t )
456  {
457  ar( t.value );
458  }
459 
460  // ######################################################################
462  template <class T> inline
463  void save( XMLOutputArchive &, SizeTag<T> const & )
464  { }
465 
467  template <class T> inline
468  void load( XMLInputArchive & ar, SizeTag<T> & st )
469  {
470  ar.loadSize( st.size );
471  }
472 
473  // ######################################################################
475  template<class T> inline
476  typename std::enable_if<std::is_arithmetic<T>::value, void>::type
477  save(XMLOutputArchive & ar, T const & t)
478  {
479  ar.saveValue( t );
480  }
481 
483  template<class T> inline
484  typename std::enable_if<std::is_arithmetic<T>::value, void>::type
485  load(XMLInputArchive & ar, T & t)
486  {
487  ar.loadValue( t );
488  }
489 
490  // ######################################################################
492  template<class CharT, class Traits, class Alloc> inline
493  void save(XMLOutputArchive & ar, std::basic_string<CharT, Traits, Alloc> const & str)
494  {
495  ar.saveValue( str );
496  }
497 
499  template<class CharT, class Traits, class Alloc> inline
500  void load(XMLInputArchive & ar, std::basic_string<CharT, Traits, Alloc> & str)
501  {
502  ar.loadValue( str );
503  }
504 } // namespace cereal
505 
506 // register archives for polymorphic support
507 CEREAL_REGISTER_ARCHIVE(cereal::XMLOutputArchive);
508 CEREAL_REGISTER_ARCHIVE(cereal::XMLInputArchive);
509 
510 #endif // CEREAL_ARCHIVES_XML_HPP_