1 /*! \file json.hpp
2     \brief JSON input and output archives */
3 /*
4   Copyright (c) 2014, 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 RANDOLPH VOORHIES OR SHANE GRANT 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_JSON_HPP_
30 #define CEREAL_ARCHIVES_JSON_HPP_
31 
32 #include "cereal/cereal.hpp"
33 #include "cereal/details/util.hpp"
34 
35 namespace cereal
36 {
37   //! An exception thrown when rapidjson fails an internal assertion
38   /*! @ingroup Utility */
39   struct RapidJSONException : Exception
RapidJSONExceptioncereal::RapidJSONException40   { RapidJSONException( const char * what_ ) : Exception( what_ ) {} };
41 }
42 
43 // Override rapidjson assertions to throw exceptions by default
44 #ifndef CEREAL_RAPIDJSON_ASSERT
45 #define CEREAL_RAPIDJSON_ASSERT(x) if(!(x)){ \
46   throw ::cereal::RapidJSONException("rapidjson internal assertion failure: " #x); }
47 #endif // RAPIDJSON_ASSERT
48 
49 // Enable support for parsing of nan, inf, -inf
50 #define CEREAL_RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNanAndInfFlag
51 #define CEREAL_RAPIDJSON_PARSE_DEFAULT_FLAGS kParseFullPrecisionFlag | kParseNanAndInfFlag
52 
53 #include "cereal/external/rapidjson/prettywriter.h"
54 #include "cereal/external/rapidjson/ostreamwrapper.h"
55 #include "cereal/external/rapidjson/istreamwrapper.h"
56 #include "cereal/external/rapidjson/document.h"
57 #include "cereal/external/base64.hpp"
58 
59 #include <limits>
60 #include <sstream>
61 #include <stack>
62 #include <vector>
63 #include <string>
64 
65 namespace cereal
66 {
67   // ######################################################################
68   //! An output archive designed to save data to JSON
69   /*! This archive uses RapidJSON to build serialize data to JSON.
70 
71       JSON archives provides a human readable output but at decreased
72       performance (both in time and space) compared to binary archives.
73 
74       JSON archives are only guaranteed to finish flushing their contents
75       upon destruction and should thus be used in an RAII fashion.
76 
77       JSON benefits greatly from name-value pairs, which if present, will
78       name the nodes in the output.  If these are not present, each level
79       of the output will be given an automatically generated delimited name.
80 
81       The precision of the output archive controls the number of decimals output
82       for floating point numbers and should be sufficiently large (i.e. at least 20)
83       if there is a desire to have binary equality between the numbers output and
84       those read in.  In general you should expect a loss of precision when going
85       from floating point to text and back.
86 
87       JSON archives do not output the size information for any dynamically sized structure
88       and instead infer it from the number of children for a node.  This means that data
89       can be hand edited for dynamic sized structures and will still be readable.  This
90       is accomplished through the cereal::SizeTag object, which will cause the archive
91       to output the data as a JSON array (e.g. marked by [] instead of {}), which indicates
92       that the container is variable sized and may be edited.
93 
94       \ingroup Archives */
95   class JSONOutputArchive : public OutputArchive<JSONOutputArchive>, public traits::TextArchive
96   {
97     enum class NodeType { StartObject, InObject, StartArray, InArray };
98 
99     using WriteStream = CEREAL_RAPIDJSON_NAMESPACE::OStreamWrapper;
100     using JSONWriter = CEREAL_RAPIDJSON_NAMESPACE::PrettyWriter<WriteStream>;
101 
102     public:
103       /*! @name Common Functionality
104           Common use cases for directly interacting with an JSONOutputArchive */
105       //! @{
106 
107       //! A class containing various advanced options for the JSON archive
108       class Options
109       {
110         public:
111           //! Default options
Default()112           static Options Default(){ return Options(); }
113 
114           //! Default options with no indentation
NoIndent()115           static Options NoIndent(){ return Options( JSONWriter::kDefaultMaxDecimalPlaces, IndentChar::space, 0 ); }
116 
117           //! The character to use for indenting
118           enum class IndentChar : char
119           {
120             space = ' ',
121             tab = '\t',
122             newline = '\n',
123             carriage_return = '\r'
124           };
125 
126           //! Specify specific options for the JSONOutputArchive
127           /*! @param precision The precision used for floating point numbers
128               @param indentChar The type of character to indent with
129               @param indentLength The number of indentChar to use for indentation
130                              (0 corresponds to no indentation) */
Options(int precision=JSONWriter::kDefaultMaxDecimalPlaces,IndentChar indentChar=IndentChar::space,unsigned int indentLength=4)131           explicit Options( int precision = JSONWriter::kDefaultMaxDecimalPlaces,
132                             IndentChar indentChar = IndentChar::space,
133                             unsigned int indentLength = 4 ) :
134             itsPrecision( precision ),
135             itsIndentChar( static_cast<char>(indentChar) ),
136             itsIndentLength( indentLength ) { }
137 
138         private:
139           friend class JSONOutputArchive;
140           int itsPrecision;
141           char itsIndentChar;
142           unsigned int itsIndentLength;
143       };
144 
145       //! Construct, outputting to the provided stream
146       /*! @param stream The stream to output to.
147           @param options The JSON specific options to use.  See the Options struct
148                          for the values of default parameters */
JSONOutputArchive(std::ostream & stream,Options const & options=Options::Default ())149       JSONOutputArchive(std::ostream & stream, Options const & options = Options::Default() ) :
150         OutputArchive<JSONOutputArchive>(this),
151         itsWriteStream(stream),
152         itsWriter(itsWriteStream),
153         itsNextName(nullptr)
154       {
155         itsWriter.SetMaxDecimalPlaces( options.itsPrecision );
156         itsWriter.SetIndent( options.itsIndentChar, options.itsIndentLength );
157         itsNameCounter.push(0);
158         itsNodeStack.push(NodeType::StartObject);
159       }
160 
161       //! Destructor, flushes the JSON
~JSONOutputArchive()162       ~JSONOutputArchive() CEREAL_NOEXCEPT
163       {
164         if (itsNodeStack.top() == NodeType::InObject)
165           itsWriter.EndObject();
166         else if (itsNodeStack.top() == NodeType::InArray)
167           itsWriter.EndArray();
168       }
169 
170       //! Saves some binary data, encoded as a base64 string, with an optional name
171       /*! This will create a new node, optionally named, and insert a value that consists of
172           the data encoded as a base64 string */
saveBinaryValue(const void * data,size_t size,const char * name=nullptr)173       void saveBinaryValue( const void * data, size_t size, const char * name = nullptr )
174       {
175         setNextName( name );
176         writeName();
177 
178         auto base64string = base64::encode( reinterpret_cast<const unsigned char *>( data ), size );
179         saveValue( base64string );
180       };
181 
182       //! @}
183       /*! @name Internal Functionality
184           Functionality designed for use by those requiring control over the inner mechanisms of
185           the JSONOutputArchive */
186       //! @{
187 
188       //! Starts a new node in the JSON output
189       /*! The node can optionally be given a name by calling setNextName prior
190           to creating the node
191 
192           Nodes only need to be started for types that are themselves objects or arrays */
startNode()193       void startNode()
194       {
195         writeName();
196         itsNodeStack.push(NodeType::StartObject);
197         itsNameCounter.push(0);
198       }
199 
200       //! Designates the most recently added node as finished
finishNode()201       void finishNode()
202       {
203         // if we ended up serializing an empty object or array, writeName
204         // will never have been called - so start and then immediately end
205         // the object/array.
206         //
207         // We'll also end any object/arrays we happen to be in
208         switch(itsNodeStack.top())
209         {
210           case NodeType::StartArray:
211             itsWriter.StartArray();
212           case NodeType::InArray:
213             itsWriter.EndArray();
214             break;
215           case NodeType::StartObject:
216             itsWriter.StartObject();
217           case NodeType::InObject:
218             itsWriter.EndObject();
219             break;
220         }
221 
222         itsNodeStack.pop();
223         itsNameCounter.pop();
224       }
225 
226       //! Sets the name for the next node created with startNode
setNextName(const char * name)227       void setNextName( const char * name )
228       {
229         itsNextName = name;
230       }
231 
232       //! Saves a bool to the current node
saveValue(bool b)233       void saveValue(bool b)                { itsWriter.Bool(b);                                                         }
234       //! Saves an int to the current node
saveValue(int i)235       void saveValue(int i)                 { itsWriter.Int(i);                                                          }
236       //! Saves a uint to the current node
saveValue(unsigned u)237       void saveValue(unsigned u)            { itsWriter.Uint(u);                                                         }
238       //! Saves an int64 to the current node
saveValue(int64_t i64)239       void saveValue(int64_t i64)           { itsWriter.Int64(i64);                                                      }
240       //! Saves a uint64 to the current node
saveValue(uint64_t u64)241       void saveValue(uint64_t u64)          { itsWriter.Uint64(u64);                                                     }
242       //! Saves a double to the current node
saveValue(double d)243       void saveValue(double d)              { itsWriter.Double(d);                                                       }
244       //! Saves a string to the current node
saveValue(std::string const & s)245       void saveValue(std::string const & s) { itsWriter.String(s.c_str(), static_cast<CEREAL_RAPIDJSON_NAMESPACE::SizeType>( s.size() )); }
246       //! Saves a const char * to the current node
saveValue(char const * s)247       void saveValue(char const * s)        { itsWriter.String(s);                                                       }
248       //! Saves a nullptr to the current node
saveValue(std::nullptr_t)249       void saveValue(std::nullptr_t)        { itsWriter.Null();                                                          }
250 
251     private:
252       // Some compilers/OS have difficulty disambiguating the above for various flavors of longs, so we provide
253       // special overloads to handle these cases.
254 
255       //! 32 bit signed long saving to current node
256       template <class T, traits::EnableIf<sizeof(T) == sizeof(std::int32_t),
257                                           std::is_signed<T>::value> = traits::sfinae> inline
saveLong(T l)258       void saveLong(T l){ saveValue( static_cast<std::int32_t>( l ) ); }
259 
260       //! non 32 bit signed long saving to current node
261       template <class T, traits::EnableIf<sizeof(T) != sizeof(std::int32_t),
262                                           std::is_signed<T>::value> = traits::sfinae> inline
saveLong(T l)263       void saveLong(T l){ saveValue( static_cast<std::int64_t>( l ) ); }
264 
265       //! 32 bit unsigned long saving to current node
266       template <class T, traits::EnableIf<sizeof(T) == sizeof(std::int32_t),
267                                           std::is_unsigned<T>::value> = traits::sfinae> inline
saveLong(T lu)268       void saveLong(T lu){ saveValue( static_cast<std::uint32_t>( lu ) ); }
269 
270       //! non 32 bit unsigned long saving to current node
271       template <class T, traits::EnableIf<sizeof(T) != sizeof(std::int32_t),
272                                           std::is_unsigned<T>::value> = traits::sfinae> inline
saveLong(T lu)273       void saveLong(T lu){ saveValue( static_cast<std::uint64_t>( lu ) ); }
274 
275     public:
276 #ifdef _MSC_VER
277       //! MSVC only long overload to current node
saveValue(unsigned long lu)278       void saveValue( unsigned long lu ){ saveLong( lu ); };
279 #else // _MSC_VER
280       //! Serialize a long if it would not be caught otherwise
281       template <class T, traits::EnableIf<std::is_same<T, long>::value,
282                                           !std::is_same<T, std::int32_t>::value,
283                                           !std::is_same<T, std::int64_t>::value> = traits::sfinae> inline
284       void saveValue( T t ){ saveLong( t ); }
285 
286       //! Serialize an unsigned long if it would not be caught otherwise
287       template <class T, traits::EnableIf<std::is_same<T, unsigned long>::value,
288                                           !std::is_same<T, std::uint32_t>::value,
289                                           !std::is_same<T, std::uint64_t>::value> = traits::sfinae> inline
290       void saveValue( T t ){ saveLong( t ); }
291 #endif // _MSC_VER
292 
293       //! Save exotic arithmetic as strings to current node
294       /*! Handles long long (if distinct from other types), unsigned long (if distinct), and long double */
295       template <class T, traits::EnableIf<std::is_arithmetic<T>::value,
296                                           !std::is_same<T, long>::value,
297                                           !std::is_same<T, unsigned long>::value,
298                                           !std::is_same<T, std::int64_t>::value,
299                                           !std::is_same<T, std::uint64_t>::value,
300                                           (sizeof(T) >= sizeof(long double) || sizeof(T) >= sizeof(long long))> = traits::sfinae> inline
saveValue(T const & t)301       void saveValue(T const & t)
302       {
303         std::stringstream ss; ss.precision( std::numeric_limits<long double>::max_digits10 );
304         ss << t;
305         saveValue( ss.str() );
306       }
307 
308       //! Write the name of the upcoming node and prepare object/array state
309       /*! Since writeName is called for every value that is output, regardless of
310           whether it has a name or not, it is the place where we will do a deferred
311           check of our node state and decide whether we are in an array or an object.
312 
313           The general workflow of saving to the JSON archive is:
314 
315             1. (optional) Set the name for the next node to be created, usually done by an NVP
316             2. Start the node
317             3. (if there is data to save) Write the name of the node (this function)
318             4. (if there is data to save) Save the data (with saveValue)
319             5. Finish the node
320           */
writeName()321       void writeName()
322       {
323         NodeType const & nodeType = itsNodeStack.top();
324 
325         // Start up either an object or an array, depending on state
326         if(nodeType == NodeType::StartArray)
327         {
328           itsWriter.StartArray();
329           itsNodeStack.top() = NodeType::InArray;
330         }
331         else if(nodeType == NodeType::StartObject)
332         {
333           itsNodeStack.top() = NodeType::InObject;
334           itsWriter.StartObject();
335         }
336 
337         // Array types do not output names
338         if(nodeType == NodeType::InArray) return;
339 
340         if(itsNextName == nullptr)
341         {
342           std::string name = "value" + std::to_string( itsNameCounter.top()++ ) + "\0";
343           saveValue(name);
344         }
345         else
346         {
347           saveValue(itsNextName);
348           itsNextName = nullptr;
349         }
350       }
351 
352       //! Designates that the current node should be output as an array, not an object
makeArray()353       void makeArray()
354       {
355         itsNodeStack.top() = NodeType::StartArray;
356       }
357 
358       //! @}
359 
360     private:
361       WriteStream itsWriteStream;          //!< Rapidjson write stream
362       JSONWriter itsWriter;                //!< Rapidjson writer
363       char const * itsNextName;            //!< The next name
364       std::stack<uint32_t> itsNameCounter; //!< Counter for creating unique names for unnamed nodes
365       std::stack<NodeType> itsNodeStack;
366   }; // JSONOutputArchive
367 
368   // ######################################################################
369   //! An input archive designed to load data from JSON
370   /*! This archive uses RapidJSON to read in a JSON archive.
371 
372       As with the output JSON archive, the preferred way to use this archive is in
373       an RAII fashion, ensuring its destruction after all data has been read.
374 
375       Input JSON should have been produced by the JSONOutputArchive.  Data can
376       only be added to dynamically sized containers (marked by JSON arrays) -
377       the input archive will determine their size by looking at the number of child nodes.
378       Only JSON originating from a JSONOutputArchive is officially supported, but data
379       from other sources may work if properly formatted.
380 
381       The JSONInputArchive does not require that nodes are loaded in the same
382       order they were saved by JSONOutputArchive.  Using name value pairs (NVPs),
383       it is possible to load in an out of order fashion or otherwise skip/select
384       specific nodes to load.
385 
386       The default behavior of the input archive is to read sequentially starting
387       with the first node and exploring its children.  When a given NVP does
388       not match the read in name for a node, the archive will search for that
389       node at the current level and load it if it exists.  After loading an out of
390       order node, the archive will then proceed back to loading sequentially from
391       its new position.
392 
393       Consider this simple example where loading of some data is skipped:
394 
395       @code{cpp}
396       // imagine the input file has someData(1-9) saved in order at the top level node
397       ar( someData1, someData2, someData3 );        // XML loads in the order it sees in the file
398       ar( cereal::make_nvp( "hello", someData6 ) ); // NVP given does not
399                                                     // match expected NVP name, so we search
400                                                     // for the given NVP and load that value
401       ar( someData7, someData8, someData9 );        // with no NVP given, loading resumes at its
402                                                     // current location, proceeding sequentially
403       @endcode
404 
405       \ingroup Archives */
406   class JSONInputArchive : public InputArchive<JSONInputArchive>, public traits::TextArchive
407   {
408     private:
409       using ReadStream = CEREAL_RAPIDJSON_NAMESPACE::IStreamWrapper;
410       typedef CEREAL_RAPIDJSON_NAMESPACE::GenericValue<CEREAL_RAPIDJSON_NAMESPACE::UTF8<>> JSONValue;
411       typedef JSONValue::ConstMemberIterator MemberIterator;
412       typedef JSONValue::ConstValueIterator ValueIterator;
413       typedef CEREAL_RAPIDJSON_NAMESPACE::Document::GenericValue GenericValue;
414 
415     public:
416       /*! @name Common Functionality
417           Common use cases for directly interacting with an JSONInputArchive */
418       //! @{
419 
420       //! Construct, reading from the provided stream
421       /*! @param stream The stream to read from */
JSONInputArchive(std::istream & stream)422       JSONInputArchive(std::istream & stream) :
423         InputArchive<JSONInputArchive>(this),
424         itsNextName( nullptr ),
425         itsReadStream(stream)
426       {
427         itsDocument.ParseStream<>(itsReadStream);
428         if (itsDocument.IsArray())
429           itsIteratorStack.emplace_back(itsDocument.Begin(), itsDocument.End());
430         else
431           itsIteratorStack.emplace_back(itsDocument.MemberBegin(), itsDocument.MemberEnd());
432       }
433 
434       ~JSONInputArchive() CEREAL_NOEXCEPT = default;
435 
436       //! Loads some binary data, encoded as a base64 string
437       /*! This will automatically start and finish a node to load the data, and can be called directly by
438           users.
439 
440           Note that this follows the same ordering rules specified in the class description in regards
441           to loading in/out of order */
loadBinaryValue(void * data,size_t size,const char * name=nullptr)442       void loadBinaryValue( void * data, size_t size, const char * name = nullptr )
443       {
444         itsNextName = name;
445 
446         std::string encoded;
447         loadValue( encoded );
448         auto decoded = base64::decode( encoded );
449 
450         if( size != decoded.size() )
451           throw Exception("Decoded binary data size does not match specified size");
452 
453         std::memcpy( data, decoded.data(), decoded.size() );
454         itsNextName = nullptr;
455       };
456 
457     private:
458       //! @}
459       /*! @name Internal Functionality
460           Functionality designed for use by those requiring control over the inner mechanisms of
461           the JSONInputArchive */
462       //! @{
463 
464       //! An internal iterator that handles both array and object types
465       /*! This class is a variant and holds both types of iterators that
466           rapidJSON supports - one for arrays and one for objects. */
467       class Iterator
468       {
469         public:
Iterator()470           Iterator() : itsIndex( 0 ), itsType(Null_) {}
471 
Iterator(MemberIterator begin,MemberIterator end)472           Iterator(MemberIterator begin, MemberIterator end) :
473             itsMemberItBegin(begin), itsMemberItEnd(end), itsIndex(0), itsType(Member)
474           {
475             if( std::distance( begin, end ) == 0 )
476               itsType = Null_;
477           }
478 
Iterator(ValueIterator begin,ValueIterator end)479           Iterator(ValueIterator begin, ValueIterator end) :
480             itsValueItBegin(begin), itsValueItEnd(end), itsIndex(0), itsType(Value)
481           {
482             if( std::distance( begin, end ) == 0 )
483               itsType = Null_;
484           }
485 
486           //! Advance to the next node
operator ++()487           Iterator & operator++()
488           {
489             ++itsIndex;
490             return *this;
491           }
492 
493           //! Get the value of the current node
value()494           GenericValue const & value()
495           {
496             switch(itsType)
497             {
498               case Value : return itsValueItBegin[itsIndex];
499               case Member: return itsMemberItBegin[itsIndex].value;
500               default: throw cereal::Exception("JSONInputArchive internal error: null or empty iterator to object or array!");
501             }
502           }
503 
504           //! Get the name of the current node, or nullptr if it has no name
name() const505           const char * name() const
506           {
507             if( itsType == Member && (itsMemberItBegin + itsIndex) != itsMemberItEnd )
508               return itsMemberItBegin[itsIndex].name.GetString();
509             else
510               return nullptr;
511           }
512 
513           //! Adjust our position such that we are at the node with the given name
514           /*! @throws Exception if no such named node exists */
search(const char * searchName)515           inline void search( const char * searchName )
516           {
517             const auto len = std::strlen( searchName );
518             size_t index = 0;
519             for( auto it = itsMemberItBegin; it != itsMemberItEnd; ++it, ++index )
520             {
521               const auto currentName = it->name.GetString();
522               if( ( std::strncmp( searchName, currentName, len ) == 0 ) &&
523                   ( std::strlen( currentName ) == len ) )
524               {
525                 itsIndex = index;
526                 return;
527               }
528             }
529 
530             throw Exception("JSON Parsing failed - provided NVP (" + std::string(searchName) + ") not found");
531           }
532 
533         private:
534           MemberIterator itsMemberItBegin, itsMemberItEnd; //!< The member iterator (object)
535           ValueIterator itsValueItBegin, itsValueItEnd;    //!< The value iterator (array)
536           size_t itsIndex;                                 //!< The current index of this iterator
537           enum Type {Value, Member, Null_} itsType;        //!< Whether this holds values (array) or members (objects) or nothing
538       };
539 
540       //! Searches for the expectedName node if it doesn't match the actualName
541       /*! This needs to be called before every load or node start occurs.  This function will
542           check to see if an NVP has been provided (with setNextName) and if so, see if that name matches the actual
543           next name given.  If the names do not match, it will search in the current level of the JSON for that name.
544           If the name is not found, an exception will be thrown.
545 
546           Resets the NVP name after called.
547 
548           @throws Exception if an expectedName is given and not found */
search()549       inline void search()
550       {
551         // The name an NVP provided with setNextName()
552         if( itsNextName )
553         {
554           // The actual name of the current node
555           auto const actualName = itsIteratorStack.back().name();
556 
557           // Do a search if we don't see a name coming up, or if the names don't match
558           if( !actualName || std::strcmp( itsNextName, actualName ) != 0 )
559             itsIteratorStack.back().search( itsNextName );
560         }
561 
562         itsNextName = nullptr;
563       }
564 
565     public:
566       //! Starts a new node, going into its proper iterator
567       /*! This places an iterator for the next node to be parsed onto the iterator stack.  If the next
568           node is an array, this will be a value iterator, otherwise it will be a member iterator.
569 
570           By default our strategy is to start with the document root node and then recursively iterate through
571           all children in the order they show up in the document.
572           We don't need to know NVPs to do this; we'll just blindly load in the order things appear in.
573 
574           If we were given an NVP, we will search for it if it does not match our the name of the next node
575           that would normally be loaded.  This functionality is provided by search(). */
startNode()576       void startNode()
577       {
578         search();
579 
580         if(itsIteratorStack.back().value().IsArray())
581           itsIteratorStack.emplace_back(itsIteratorStack.back().value().Begin(), itsIteratorStack.back().value().End());
582         else
583           itsIteratorStack.emplace_back(itsIteratorStack.back().value().MemberBegin(), itsIteratorStack.back().value().MemberEnd());
584       }
585 
586       //! Finishes the most recently started node
finishNode()587       void finishNode()
588       {
589         itsIteratorStack.pop_back();
590         ++itsIteratorStack.back();
591       }
592 
593       //! Retrieves the current node name
594       /*! @return nullptr if no name exists */
getNodeName() const595       const char * getNodeName() const
596       {
597         return itsIteratorStack.back().name();
598       }
599 
600       //! Sets the name for the next node created with startNode
setNextName(const char * name)601       void setNextName( const char * name )
602       {
603         itsNextName = name;
604       }
605 
606       //! Loads a value from the current node - small signed overload
607       template <class T, traits::EnableIf<std::is_signed<T>::value,
608                                           sizeof(T) < sizeof(int64_t)> = traits::sfinae> inline
loadValue(T & val)609       void loadValue(T & val)
610       {
611         search();
612 
613         val = static_cast<T>( itsIteratorStack.back().value().GetInt() );
614         ++itsIteratorStack.back();
615       }
616 
617       //! Loads a value from the current node - small unsigned overload
618       template <class T, traits::EnableIf<std::is_unsigned<T>::value,
619                                           sizeof(T) < sizeof(uint64_t),
620                                           !std::is_same<bool, T>::value> = traits::sfinae> inline
loadValue(T & val)621       void loadValue(T & val)
622       {
623         search();
624 
625         val = static_cast<T>( itsIteratorStack.back().value().GetUint() );
626         ++itsIteratorStack.back();
627       }
628 
629       //! Loads a value from the current node - bool overload
loadValue(bool & val)630       void loadValue(bool & val)        { search(); val = itsIteratorStack.back().value().GetBool(); ++itsIteratorStack.back(); }
631       //! Loads a value from the current node - int64 overload
loadValue(int64_t & val)632       void loadValue(int64_t & val)     { search(); val = itsIteratorStack.back().value().GetInt64(); ++itsIteratorStack.back(); }
633       //! Loads a value from the current node - uint64 overload
loadValue(uint64_t & val)634       void loadValue(uint64_t & val)    { search(); val = itsIteratorStack.back().value().GetUint64(); ++itsIteratorStack.back(); }
635       //! Loads a value from the current node - float overload
loadValue(float & val)636       void loadValue(float & val)       { search(); val = static_cast<float>(itsIteratorStack.back().value().GetDouble()); ++itsIteratorStack.back(); }
637       //! Loads a value from the current node - double overload
loadValue(double & val)638       void loadValue(double & val)      { search(); val = itsIteratorStack.back().value().GetDouble(); ++itsIteratorStack.back(); }
639       //! Loads a value from the current node - string overload
loadValue(std::string & val)640       void loadValue(std::string & val) { search(); val = itsIteratorStack.back().value().GetString(); ++itsIteratorStack.back(); }
641       //! Loads a nullptr from the current node
loadValue(std::nullptr_t &)642       void loadValue(std::nullptr_t&)   { search(); CEREAL_RAPIDJSON_ASSERT(itsIteratorStack.back().value().IsNull()); ++itsIteratorStack.back(); }
643 
644       // Special cases to handle various flavors of long, which tend to conflict with
645       // the int32_t or int64_t on various compiler/OS combinations.  MSVC doesn't need any of this.
646       #ifndef _MSC_VER
647     private:
648       //! 32 bit signed long loading from current node
649       template <class T> inline
650       typename std::enable_if<sizeof(T) == sizeof(std::int32_t) && std::is_signed<T>::value, void>::type
loadLong(T & l)651       loadLong(T & l){ loadValue( reinterpret_cast<std::int32_t&>( l ) ); }
652 
653       //! non 32 bit signed long loading from current node
654       template <class T> inline
655       typename std::enable_if<sizeof(T) == sizeof(std::int64_t) && std::is_signed<T>::value, void>::type
loadLong(T & l)656       loadLong(T & l){ loadValue( reinterpret_cast<std::int64_t&>( l ) ); }
657 
658       //! 32 bit unsigned long loading from current node
659       template <class T> inline
660       typename std::enable_if<sizeof(T) == sizeof(std::uint32_t) && !std::is_signed<T>::value, void>::type
loadLong(T & lu)661       loadLong(T & lu){ loadValue( reinterpret_cast<std::uint32_t&>( lu ) ); }
662 
663       //! non 32 bit unsigned long loading from current node
664       template <class T> inline
665       typename std::enable_if<sizeof(T) == sizeof(std::uint64_t) && !std::is_signed<T>::value, void>::type
loadLong(T & lu)666       loadLong(T & lu){ loadValue( reinterpret_cast<std::uint64_t&>( lu ) ); }
667 
668     public:
669       //! Serialize a long if it would not be caught otherwise
670       template <class T> inline
671       typename std::enable_if<std::is_same<T, long>::value &&
672                               sizeof(T) >= sizeof(std::int64_t) &&
673                               !std::is_same<T, std::int64_t>::value, void>::type
674       loadValue( T & t ){ loadLong(t); }
675 
676       //! Serialize an unsigned long if it would not be caught otherwise
677       template <class T> inline
678       typename std::enable_if<std::is_same<T, unsigned long>::value &&
679                               sizeof(T) >= sizeof(std::uint64_t) &&
680                               !std::is_same<T, std::uint64_t>::value, void>::type
681       loadValue( T & t ){ loadLong(t); }
682       #endif // _MSC_VER
683 
684     private:
685       //! Convert a string to a long long
stringToNumber(std::string const & str,long long & val)686       void stringToNumber( std::string const & str, long long & val ) { val = std::stoll( str ); }
687       //! Convert a string to an unsigned long long
stringToNumber(std::string const & str,unsigned long long & val)688       void stringToNumber( std::string const & str, unsigned long long & val ) { val = std::stoull( str ); }
689       //! Convert a string to a long double
stringToNumber(std::string const & str,long double & val)690       void stringToNumber( std::string const & str, long double & val ) { val = std::stold( str ); }
691 
692     public:
693       //! Loads a value from the current node - long double and long long overloads
694       template <class T, traits::EnableIf<std::is_arithmetic<T>::value,
695                                           !std::is_same<T, long>::value,
696                                           !std::is_same<T, unsigned long>::value,
697                                           !std::is_same<T, std::int64_t>::value,
698                                           !std::is_same<T, std::uint64_t>::value,
699                                           (sizeof(T) >= sizeof(long double) || sizeof(T) >= sizeof(long long))> = traits::sfinae>
loadValue(T & val)700       inline void loadValue(T & val)
701       {
702         std::string encoded;
703         loadValue( encoded );
704         stringToNumber( encoded, val );
705       }
706 
707       //! Loads the size for a SizeTag
loadSize(size_type & size)708       void loadSize(size_type & size)
709       {
710         if (itsIteratorStack.size() == 1)
711           size = itsDocument.Size();
712         else
713           size = (itsIteratorStack.rbegin() + 1)->value().Size();
714       }
715 
716       //! @}
717 
718     private:
719       const char * itsNextName;               //!< Next name set by NVP
720       ReadStream itsReadStream;               //!< Rapidjson write stream
721       std::vector<Iterator> itsIteratorStack; //!< 'Stack' of rapidJSON iterators
722       CEREAL_RAPIDJSON_NAMESPACE::Document itsDocument; //!< Rapidjson document
723   };
724 
725   // ######################################################################
726   // JSONArchive prologue and epilogue functions
727   // ######################################################################
728 
729   // ######################################################################
730   //! Prologue for NVPs for JSON archives
731   /*! NVPs do not start or finish nodes - they just set up the names */
732   template <class T> inline
prologue(JSONOutputArchive &,NameValuePair<T> const &)733   void prologue( JSONOutputArchive &, NameValuePair<T> const & )
734   { }
735 
736   //! Prologue for NVPs for JSON archives
737   template <class T> inline
prologue(JSONInputArchive &,NameValuePair<T> const &)738   void prologue( JSONInputArchive &, NameValuePair<T> const & )
739   { }
740 
741   // ######################################################################
742   //! Epilogue for NVPs for JSON archives
743   /*! NVPs do not start or finish nodes - they just set up the names */
744   template <class T> inline
epilogue(JSONOutputArchive &,NameValuePair<T> const &)745   void epilogue( JSONOutputArchive &, NameValuePair<T> const & )
746   { }
747 
748   //! Epilogue for NVPs for JSON archives
749   /*! NVPs do not start or finish nodes - they just set up the names */
750   template <class T> inline
epilogue(JSONInputArchive &,NameValuePair<T> const &)751   void epilogue( JSONInputArchive &, NameValuePair<T> const & )
752   { }
753 
754   // ######################################################################
755   //! Prologue for SizeTags for JSON archives
756   /*! SizeTags are strictly ignored for JSON, they just indicate
757       that the current node should be made into an array */
758   template <class T> inline
prologue(JSONOutputArchive & ar,SizeTag<T> const &)759   void prologue( JSONOutputArchive & ar, SizeTag<T> const & )
760   {
761     ar.makeArray();
762   }
763 
764   //! Prologue for SizeTags for JSON archives
765   template <class T> inline
prologue(JSONInputArchive &,SizeTag<T> const &)766   void prologue( JSONInputArchive &, SizeTag<T> const & )
767   { }
768 
769   // ######################################################################
770   //! Epilogue for SizeTags for JSON archives
771   /*! SizeTags are strictly ignored for JSON */
772   template <class T> inline
epilogue(JSONOutputArchive &,SizeTag<T> const &)773   void epilogue( JSONOutputArchive &, SizeTag<T> const & )
774   { }
775 
776   //! Epilogue for SizeTags for JSON archives
777   template <class T> inline
epilogue(JSONInputArchive &,SizeTag<T> const &)778   void epilogue( JSONInputArchive &, SizeTag<T> const & )
779   { }
780 
781   // ######################################################################
782   //! Prologue for all other types for JSON archives (except minimal types)
783   /*! Starts a new node, named either automatically or by some NVP,
784       that may be given data by the type about to be archived
785 
786       Minimal types do not start or finish nodes */
787   template <class T, traits::EnableIf<!std::is_arithmetic<T>::value,
788                                       !traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, JSONOutputArchive>::value,
789                                       !traits::has_minimal_output_serialization<T, JSONOutputArchive>::value> = traits::sfinae>
prologue(JSONOutputArchive & ar,T const &)790   inline void prologue( JSONOutputArchive & ar, T const & )
791   {
792     ar.startNode();
793   }
794 
795   //! Prologue for all other types for JSON archives
796   template <class T, traits::EnableIf<!std::is_arithmetic<T>::value,
797                                       !traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, JSONInputArchive>::value,
798                                       !traits::has_minimal_input_serialization<T, JSONInputArchive>::value> = traits::sfinae>
prologue(JSONInputArchive & ar,T const &)799   inline void prologue( JSONInputArchive & ar, T const & )
800   {
801     ar.startNode();
802   }
803 
804   // ######################################################################
805   //! Epilogue for all other types other for JSON archives (except minimal types)
806   /*! Finishes the node created in the prologue
807 
808       Minimal types do not start or finish nodes */
809   template <class T, traits::EnableIf<!std::is_arithmetic<T>::value,
810                                       !traits::has_minimal_base_class_serialization<T, traits::has_minimal_output_serialization, JSONOutputArchive>::value,
811                                       !traits::has_minimal_output_serialization<T, JSONOutputArchive>::value> = traits::sfinae>
epilogue(JSONOutputArchive & ar,T const &)812   inline void epilogue( JSONOutputArchive & ar, T const & )
813   {
814     ar.finishNode();
815   }
816 
817   //! Epilogue for all other types other for JSON archives
818   template <class T, traits::EnableIf<!std::is_arithmetic<T>::value,
819                                       !traits::has_minimal_base_class_serialization<T, traits::has_minimal_input_serialization, JSONInputArchive>::value,
820                                       !traits::has_minimal_input_serialization<T, JSONInputArchive>::value> = traits::sfinae>
epilogue(JSONInputArchive & ar,T const &)821   inline void epilogue( JSONInputArchive & ar, T const & )
822   {
823     ar.finishNode();
824   }
825 
826   // ######################################################################
827   //! Prologue for arithmetic types for JSON archives
828   inline
prologue(JSONOutputArchive & ar,std::nullptr_t const &)829   void prologue( JSONOutputArchive & ar, std::nullptr_t const & )
830   {
831     ar.writeName();
832   }
833 
834   //! Prologue for arithmetic types for JSON archives
835   inline
prologue(JSONInputArchive &,std::nullptr_t const &)836   void prologue( JSONInputArchive &, std::nullptr_t const & )
837   { }
838 
839   // ######################################################################
840   //! Epilogue for arithmetic types for JSON archives
841   inline
epilogue(JSONOutputArchive &,std::nullptr_t const &)842   void epilogue( JSONOutputArchive &, std::nullptr_t const & )
843   { }
844 
845   //! Epilogue for arithmetic types for JSON archives
846   inline
epilogue(JSONInputArchive &,std::nullptr_t const &)847   void epilogue( JSONInputArchive &, std::nullptr_t const & )
848   { }
849 
850   // ######################################################################
851   //! Prologue for arithmetic types for JSON archives
852   template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
prologue(JSONOutputArchive & ar,T const &)853   void prologue( JSONOutputArchive & ar, T const & )
854   {
855     ar.writeName();
856   }
857 
858   //! Prologue for arithmetic types for JSON archives
859   template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
prologue(JSONInputArchive &,T const &)860   void prologue( JSONInputArchive &, T const & )
861   { }
862 
863   // ######################################################################
864   //! Epilogue for arithmetic types for JSON archives
865   template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
epilogue(JSONOutputArchive &,T const &)866   void epilogue( JSONOutputArchive &, T const & )
867   { }
868 
869   //! Epilogue for arithmetic types for JSON archives
870   template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
epilogue(JSONInputArchive &,T const &)871   void epilogue( JSONInputArchive &, T const & )
872   { }
873 
874   // ######################################################################
875   //! Prologue for strings for JSON archives
876   template<class CharT, class Traits, class Alloc> inline
prologue(JSONOutputArchive & ar,std::basic_string<CharT,Traits,Alloc> const &)877   void prologue(JSONOutputArchive & ar, std::basic_string<CharT, Traits, Alloc> const &)
878   {
879     ar.writeName();
880   }
881 
882   //! Prologue for strings for JSON archives
883   template<class CharT, class Traits, class Alloc> inline
prologue(JSONInputArchive &,std::basic_string<CharT,Traits,Alloc> const &)884   void prologue(JSONInputArchive &, std::basic_string<CharT, Traits, Alloc> const &)
885   { }
886 
887   // ######################################################################
888   //! Epilogue for strings for JSON archives
889   template<class CharT, class Traits, class Alloc> inline
epilogue(JSONOutputArchive &,std::basic_string<CharT,Traits,Alloc> const &)890   void epilogue(JSONOutputArchive &, std::basic_string<CharT, Traits, Alloc> const &)
891   { }
892 
893   //! Epilogue for strings for JSON archives
894   template<class CharT, class Traits, class Alloc> inline
epilogue(JSONInputArchive &,std::basic_string<CharT,Traits,Alloc> const &)895   void epilogue(JSONInputArchive &, std::basic_string<CharT, Traits, Alloc> const &)
896   { }
897 
898   // ######################################################################
899   // Common JSONArchive serialization functions
900   // ######################################################################
901   //! Serializing NVP types to JSON
902   template <class T> inline
CEREAL_SAVE_FUNCTION_NAME(JSONOutputArchive & ar,NameValuePair<T> const & t)903   void CEREAL_SAVE_FUNCTION_NAME( JSONOutputArchive & ar, NameValuePair<T> const & t )
904   {
905     ar.setNextName( t.name );
906     ar( t.value );
907   }
908 
909   template <class T> inline
CEREAL_LOAD_FUNCTION_NAME(JSONInputArchive & ar,NameValuePair<T> & t)910   void CEREAL_LOAD_FUNCTION_NAME( JSONInputArchive & ar, NameValuePair<T> & t )
911   {
912     ar.setNextName( t.name );
913     ar( t.value );
914   }
915 
916   //! Saving for nullptr to JSON
917   inline
CEREAL_SAVE_FUNCTION_NAME(JSONOutputArchive & ar,std::nullptr_t const & t)918   void CEREAL_SAVE_FUNCTION_NAME(JSONOutputArchive & ar, std::nullptr_t const & t)
919   {
920     ar.saveValue( t );
921   }
922 
923   //! Loading arithmetic from JSON
924   inline
CEREAL_LOAD_FUNCTION_NAME(JSONInputArchive & ar,std::nullptr_t & t)925   void CEREAL_LOAD_FUNCTION_NAME(JSONInputArchive & ar, std::nullptr_t & t)
926   {
927     ar.loadValue( t );
928   }
929 
930   //! Saving for arithmetic to JSON
931   template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
CEREAL_SAVE_FUNCTION_NAME(JSONOutputArchive & ar,T const & t)932   void CEREAL_SAVE_FUNCTION_NAME(JSONOutputArchive & ar, T const & t)
933   {
934     ar.saveValue( t );
935   }
936 
937   //! Loading arithmetic from JSON
938   template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
CEREAL_LOAD_FUNCTION_NAME(JSONInputArchive & ar,T & t)939   void CEREAL_LOAD_FUNCTION_NAME(JSONInputArchive & ar, T & t)
940   {
941     ar.loadValue( t );
942   }
943 
944   //! saving string to JSON
945   template<class CharT, class Traits, class Alloc> inline
CEREAL_SAVE_FUNCTION_NAME(JSONOutputArchive & ar,std::basic_string<CharT,Traits,Alloc> const & str)946   void CEREAL_SAVE_FUNCTION_NAME(JSONOutputArchive & ar, std::basic_string<CharT, Traits, Alloc> const & str)
947   {
948     ar.saveValue( str );
949   }
950 
951   //! loading string from JSON
952   template<class CharT, class Traits, class Alloc> inline
CEREAL_LOAD_FUNCTION_NAME(JSONInputArchive & ar,std::basic_string<CharT,Traits,Alloc> & str)953   void CEREAL_LOAD_FUNCTION_NAME(JSONInputArchive & ar, std::basic_string<CharT, Traits, Alloc> & str)
954   {
955     ar.loadValue( str );
956   }
957 
958   // ######################################################################
959   //! Saving SizeTags to JSON
960   template <class T> inline
CEREAL_SAVE_FUNCTION_NAME(JSONOutputArchive &,SizeTag<T> const &)961   void CEREAL_SAVE_FUNCTION_NAME( JSONOutputArchive &, SizeTag<T> const & )
962   {
963     // nothing to do here, we don't explicitly save the size
964   }
965 
966   //! Loading SizeTags from JSON
967   template <class T> inline
CEREAL_LOAD_FUNCTION_NAME(JSONInputArchive & ar,SizeTag<T> & st)968   void CEREAL_LOAD_FUNCTION_NAME( JSONInputArchive & ar, SizeTag<T> & st )
969   {
970     ar.loadSize( st.size );
971   }
972 } // namespace cereal
973 
974 // register archives for polymorphic support
975 CEREAL_REGISTER_ARCHIVE(cereal::JSONInputArchive)
976 CEREAL_REGISTER_ARCHIVE(cereal::JSONOutputArchive)
977 
978 // tie input and output archives together
979 CEREAL_SETUP_ARCHIVE_TRAITS(cereal::JSONInputArchive, cereal::JSONOutputArchive)
980 
981 #endif // CEREAL_ARCHIVES_JSON_HPP_
982