Fix for the Jira issue IOT-524
[iotivity.git] / resource / include / OicJsonSerializer.hpp
1 /*! \file OicJsonSerializer.hpp
2     \brief JSON input and output archives.
3 Note: this has been customized by Intel(R) for usage in the OIC project.
4 Nearly the entire file is from Cereal (see copyright notice below) other than specified
5 below
6 Added:
7 #include of AttributeValue Type
8 JSONOutputArchive::saveValue() to add JSON null value
9 loadAttributeValues to get attribute values out of a map (implemented in OCRepresentation)
10
11 */
12 /*
13   Copyright (c) 2014, Randolph Voorhies, Shane Grant
14   All rights reserved.
15
16   Redistribution and use in source and binary forms, with or without
17   modification, are permitted provided that the following conditions are met:
18       * Redistributions of source code must retain the above copyright
19         notice, this list of conditions and the following disclaimer.
20       * Redistributions in binary form must reproduce the above copyright
21         notice, this list of conditions and the following disclaimer in the
22         documentation and/or other materials provided with the distribution.
23       * Neither the name of cereal nor the
24         names of its contributors may be used to endorse or promote products
25         derived from this software without specific prior written permission.
26
27   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
28   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
29   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
30   DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY
31   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
33   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
34   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 #ifndef CEREAL_ARCHIVES_JSON_HPP_
40 #define CEREAL_ARCHIVES_JSON_HPP_
41
42 #include <AttributeValue.h>
43 #include <cereal/cereal.hpp>
44 #include <cereal/details/util.hpp>
45 namespace cereal
46 {
47   //! An exception thrown when rapidjson fails an internal assertion
48   /*! @ingroup Utility */
49   struct RapidJSONException : Exception
50   { RapidJSONException( const char * what_ ) : Exception( what_ ) {} };
51 }
52
53 // Override rapidjson assertions to throw exceptions by default
54 #ifndef RAPIDJSON_ASSERT
55 #define RAPIDJSON_ASSERT(x) if(!(x)){ \
56   throw ::cereal::RapidJSONException("rapidjson internal assertion failure: " #x); }
57 #endif // RAPIDJSON_ASSERT
58
59 #include <cereal/external/rapidjson/writer.h>
60 #include <cereal/external/rapidjson/genericstream.h>
61 #include <cereal/external/rapidjson/reader.h>
62 #include <cereal/external/rapidjson/document.h>
63 #include <cereal/external/base64.hpp>
64
65 #include <limits>
66 #include <sstream>
67 #include <stack>
68 #include <vector>
69 #include <string>
70
71 namespace cereal
72 {
73   // ######################################################################
74   //! An output archive designed to save data to JSON
75   /*! This archive uses RapidJSON to build serialized data to JSON.
76
77       JSON archives provides a human readable output but at decreased
78       performance (both in time and space) compared to binary archives.
79
80       JSON benefits greatly from name-value pairs, which if present, will
81       name the nodes in the output.  If these are not present, each level
82       of the output will be given an automatically generated delimited name.
83
84       The precision of the output archive controls the number of decimals output
85       for floating point numbers and should be sufficiently large (i.e. at least 20)
86       if there is a desire to have binary equality between the numbers output and
87       those read in.  In general you should expect a loss of precision when going
88       from floating point to text and back.
89
90       JSON archives do not output the size information for any dynamically sized structure
91       and instead infer it from the number of children for a node.  This means that data
92       can be hand edited for dynamic sized structures and will still be readable.  This
93       is accomplished through the cereal::SizeTag object, which will cause the archive
94       to output the data as a JSON array (e.g. marked by [] instead of {}), which indicates
95       that the container is variable sized and may be edited.
96
97       \ingroup Archives */
98   class JSONOutputArchive : public OutputArchive<JSONOutputArchive>
99   {
100     enum class NodeType { StartObject, InObject, StartArray, InArray };
101
102     typedef rapidjson::GenericWriteStream WriteStream;
103     typedef rapidjson::Writer<WriteStream> JSONWriter;
104
105     public:
106       /*! @name Common Functionality
107           Common use cases for directly interacting with an JSONOutputArchive */
108       //! @{
109
110       //! A class containing various advanced options for the JSON archive
111       class Options
112       {
113         public:
114           //! Default options
115           static Options Default(){ return Options(); }
116
117           //! Specify specific options for the JSONOutputArchive
118           /*! @param precision The precision used for floating point numbers*/
119           explicit Options( int precision = std::numeric_limits<double>::max_digits10) :
120             itsPrecision( precision ) { }
121
122         private:
123           friend class JSONOutputArchive;
124           int itsPrecision;
125       };
126
127       //! Construct, outputting to the provided stream
128       /*! @param stream The stream to output to.
129           @param options The JSON specific options to use.  See the Options struct
130                          for the values of default parameters */
131       JSONOutputArchive(std::ostream & stream, Options const & options = Options::Default() ) :
132         OutputArchive<JSONOutputArchive>(this),
133         itsWriteStream(stream),
134         itsWriter(itsWriteStream, options.itsPrecision),
135         itsNextName(nullptr)
136       {
137         itsNameCounter.push(0);
138         itsNodeStack.push(NodeType::StartObject);
139       }
140
141       //! Destructor, flushes the JSON
142       ~JSONOutputArchive()
143       {
144         itsWriter.EndObject();
145       }
146
147       //! Saves some binary data, encoded as a base64 string, with an optional name
148       /*! This will create a new node, optionally named, and insert a value that consists of
149           the data encoded as a base64 string */
150       void saveBinaryValue( const void * data, size_t size, const char * name = nullptr )
151       {
152         setNextName( name );
153         writeName();
154
155         auto base64string = base64::encode( reinterpret_cast<const unsigned char *>( data ), size );
156         saveValue( base64string );
157       };
158
159       //! @}
160       /*! @name Internal Functionality
161           Functionality designed for use by those requiring control over the inner mechanisms of
162           the JSONOutputArchive */
163       //! @{
164
165       //! Starts a new node in the JSON output
166       /*! The node can optionally be given a name by calling setNextName prior
167           to creating the node
168
169           Nodes only need to be started for types that are themselves objects or arrays */
170       void startNode()
171       {
172         writeName();
173         itsNodeStack.push(NodeType::StartObject);
174         itsNameCounter.push(0);
175       }
176
177       //! Designates the most recently added node as finished
178       void finishNode()
179       {
180         // if we ended up serializing an empty object or array, writeName
181         // will never have been called - so start and then immediately end
182         // the object/array.
183         //
184         // We'll also end any object/arrays we happen to be in
185         switch(itsNodeStack.top())
186         {
187           case NodeType::StartArray:
188             itsWriter.StartArray();
189           case NodeType::InArray:
190             itsWriter.EndArray();
191             break;
192           case NodeType::StartObject:
193             itsWriter.StartObject();
194           case NodeType::InObject:
195             itsWriter.EndObject();
196             break;
197         }
198
199         itsNodeStack.pop();
200         itsNameCounter.pop();
201       }
202
203       //! Sets the name for the next node created with startNode
204       void setNextName( const char * name )
205       {
206         itsNextName = name;
207       }
208
209       //! Saves a null to the current node, added by Intel
210       void saveValue()                      { itsWriter.Null_();                                                         }
211       //! Saves a bool to the current node
212       void saveValue(bool b)                { itsWriter.Bool_(b);                                                         }
213       //! Saves an int to the current node
214       void saveValue(int i)                 { itsWriter.Int(i);                                                          }
215       //! Saves a uint to the current node
216       void saveValue(unsigned u)            { itsWriter.Uint(u);                                                         }
217       //! Saves an int64 to the current node
218       void saveValue(int64_t i64)           { itsWriter.Int64(i64);                                                      }
219       //! Saves a uint64 to the current node
220       void saveValue(uint64_t u64)          { itsWriter.Uint64(u64);                                                     }
221       //! Saves a double to the current node
222       void saveValue(double d)              { itsWriter.Double(d);                                                       }
223       //! Saves a string to the current node
224       void saveValue(std::string const & s) { itsWriter.String(s.c_str(), static_cast<rapidjson::SizeType>( s.size() )); }
225       //! Saves a const char * to the current node
226       void saveValue(char const * s)        { itsWriter.String(s);                                                       }
227
228     public:
229 #ifdef _MSC_VER
230       //! MSVC only long overload to current node
231       void saveValue( unsigned long lu ){ saveLong( lu ); };
232 #else // _MSC_VER
233       //! Serialize a long if it would not be caught otherwise
234       template <class T> inline
235       typename std::enable_if<std::is_same<T, long>::value &&
236                               !std::is_same<T, std::int32_t>::value &&
237                               !std::is_same<T, std::int64_t>::value, void>::type
238       saveValue( T t )
239       {
240           saveLong( t );
241           return t;
242       }
243
244       //! Serialize an unsigned long if it would not be caught otherwise
245       template <class T> inline
246       typename std::enable_if<std::is_same<T, unsigned long>::value &&
247                               !std::is_same<T, std::uint32_t>::value &&
248                               !std::is_same<T, std::uint64_t>::value, void>::type
249       saveValue( T t )
250       {
251           saveLong( t );
252           return t;
253       }
254 #endif // _MSC_VER
255
256       //! Save exotic arithmetic as strings to current node
257       /*! Handles long long (if distinct from other types), unsigned long (if distinct), and long double */
258       template<class T> inline
259       typename std::enable_if<std::is_arithmetic<T>::value &&
260                               !std::is_same<T, long>::value &&
261                               !std::is_same<T, unsigned long>::value &&
262                               !std::is_same<T, std::int64_t>::value &&
263                               !std::is_same<T, std::uint64_t>::value &&
264                               (sizeof(T) >= sizeof(long double) || sizeof(T) >= sizeof(long long)), void>::type
265       saveValue(T const & t)
266       {
267         std::stringstream ss; ss.precision( std::numeric_limits<long double>::max_digits10 );
268         ss << t;
269         saveValue( ss.str() );
270         return t;
271       }
272
273       //! Write the name of the upcoming node and prepare object/array state
274       /*! Since writeName is called for every value that is output, regardless of
275           whether it has a name or not, it is the place where we will do a deferred
276           check of our node state and decide whether we are in an array or an object.
277
278           The general workflow of saving to the JSON archive is:
279
280             1. (optional) Set the name for the next node to be created, usually done by an NVP
281             2. Start the node
282             3. (if there is data to save) Write the name of the node (this function)
283             4. (if there is data to save) Save the data (with saveValue)
284             5. Finish the node
285           */
286       void writeName()
287       {
288         NodeType const & nodeType = itsNodeStack.top();
289
290         // Start up either an object or an array, depending on state
291         if(nodeType == NodeType::StartArray)
292         {
293           itsWriter.StartArray();
294           itsNodeStack.top() = NodeType::InArray;
295         }
296         else if(nodeType == NodeType::StartObject)
297         {
298           itsNodeStack.top() = NodeType::InObject;
299           itsWriter.StartObject();
300         }
301
302         // Array types do not output names
303         if(nodeType == NodeType::InArray) return;
304
305         if(itsNextName == nullptr)
306         {
307           std::string name = "value" + std::to_string( itsNameCounter.top()++ ) + "\0";
308           saveValue(name);
309         }
310         else
311         {
312           saveValue(itsNextName);
313           itsNextName = nullptr;
314         }
315       }
316
317       //! Designates that the current node should be output as an array, not an object
318       void makeArray()
319       {
320         itsNodeStack.top() = NodeType::StartArray;
321       }
322
323       //! @}
324
325     private:
326       WriteStream itsWriteStream;          //!< Rapidjson write stream
327       JSONWriter itsWriter;                //!< Rapidjson writer
328       char const * itsNextName;            //!< The next name
329       std::stack<uint32_t> itsNameCounter; //!< Counter for creating unique names for unnamed nodes
330       std::stack<NodeType> itsNodeStack;
331   }; // JSONOutputArchive
332
333   // ######################################################################
334   //! An input archive designed to load data from JSON
335   /*! This archive uses RapidJSON to read in a JSON archive.
336
337       Input JSON should have been produced by the JSONOutputArchive.  Data can
338       only be added to dynamically sized containers (marked by JSON arrays) -
339       the input archive will determine their size by looking at the number of child nodes.
340       Only JSON originating from a JSONOutputArchive is officially supported, but data
341       from other sources may work if properly formatted.
342
343       The JSONInputArchive does not require that nodes are loaded in the same
344       order they were saved by JSONOutputArchive.  Using name value pairs (NVPs),
345       it is possible to load in an out of order fashion or otherwise skip/select
346       specific nodes to load.
347
348       The default behavior of the input archive is to read sequentially starting
349       with the first node and exploring its children.  When a given NVP does
350       not match the read in name for a node, the archive will search for that
351       node at the current level and load it if it exists.  After loading an out of
352       order node, the archive will then proceed back to loading sequentially from
353       its new position.
354
355       Consider this simple example where loading of some data is skipped:
356
357       @code{cpp}
358       // imagine the input file has someData(1-9) saved in order at the top level node
359       ar( someData1, someData2, someData3 );        // XML loads in the order it sees in the file
360       ar( cereal::make_nvp( "hello", someData6 ) ); // NVP given does not
361                                                     // match expected NVP name, so we search
362                                                     // for the given NVP and load that value
363       ar( someData7, someData8, someData9 );        // with no NVP given, loading resumes at its
364                                                     // current location, proceeding sequentially
365       @endcode
366
367       \ingroup Archives */
368   class JSONInputArchive : public InputArchive<JSONInputArchive>
369   {
370     private:
371       typedef rapidjson::GenericReadStream ReadStream;
372       typedef rapidjson::GenericValue<rapidjson::UTF8<>> JSONValue;
373       typedef JSONValue::ConstMemberIterator MemberIterator;
374       typedef JSONValue::ConstValueIterator ValueIterator;
375       typedef rapidjson::Document::GenericValue GenericValue;
376
377     public:
378       /*! @name Common Functionality
379           Common use cases for directly interacting with an JSONInputArchive */
380       //! @{
381
382       //! Construct, reading from the provided stream
383       /*! @param stream The stream to read from */
384       JSONInputArchive(std::istream & stream) :
385         InputArchive<JSONInputArchive>(this),
386         itsNextName( nullptr ),
387         itsReadStream(stream)
388       {
389         itsDocument.ParseStream<0>(itsReadStream);
390         itsIteratorStack.emplace_back(itsDocument.MemberBegin(), itsDocument.MemberEnd());
391       }
392
393       //! Loads some binary data, encoded as a base64 string
394       /*! This will automatically start and finish a node to load the data, and can be called directly by
395           users.
396
397           Note that this follows the same ordering rules specified in the class description in regards
398           to loading in/out of order */
399       void loadBinaryValue( void * data, size_t size, const char * name = nullptr )
400       {
401         itsNextName = name;
402
403         std::string encoded;
404         loadValue( encoded );
405         auto decoded = base64::decode( encoded );
406
407         if( size != decoded.size() )
408           throw Exception("Decoded binary data size does not match specified size");
409
410         std::memcpy( data, decoded.data(), decoded.size() );
411         itsNextName = nullptr;
412       };
413
414       // Intel Added this as a custom parsing hook for the AttributeValue map
415       void loadAttributeValues(std::map<std::string, OC::AttributeValue>& map);
416
417     private:
418       //! @}
419       /*! @name Internal Functionality
420           Functionality designed for use by those requiring control over the inner mechanisms of
421           the JSONInputArchive */
422       //! @{
423
424       //! An internal iterator that handles both array and object types
425       /*! This class is a variant and holds both types of iterators that
426           rapidJSON supports - one for arrays and one for objects. */
427       class Iterator
428       {
429         public:
430           friend class cereal::JSONInputArchive;
431           Iterator() : itsIndex( 0 ), itsType(Null_) {}
432
433           Iterator(MemberIterator begin, MemberIterator end) :
434             itsMemberItBegin(begin), itsMemberItEnd(end), itsIndex(0), itsType(Member)
435           { }
436
437           Iterator(ValueIterator begin, ValueIterator end) :
438             itsValueItBegin(begin), itsValueItEnd(end), itsIndex(0), itsType(Value)
439           { }
440
441           //! Advance to the next node
442           Iterator & operator++()
443           {
444             ++itsIndex;
445             return *this;
446           }
447
448           //! Get the value of the current node
449           GenericValue const & value()
450           {
451             switch(itsType)
452             {
453               case Value : return itsValueItBegin[itsIndex];
454               case Member: return itsMemberItBegin[itsIndex].value;
455               default: throw cereal::Exception("Invalid Iterator Type!");
456             }
457           }
458
459           //! Get the name of the current node, or nullptr if it has no name
460           const char * name() const
461           {
462             if( itsType == Member && (itsMemberItBegin + itsIndex) != itsMemberItEnd )
463               return itsMemberItBegin[itsIndex].name.GetString();
464             else
465               return nullptr;
466           }
467
468           //! Adjust our position such that we are at the node with the given name
469           /*! @throws Exception if no such named node exists */
470           inline void search( const char * searchName )
471           {
472             const auto len = std::strlen( searchName );
473             size_t index = 0;
474             for( auto it = itsMemberItBegin; it != itsMemberItEnd; ++it, ++index )
475               if( std::strncmp( searchName, it->name.GetString(), len ) == 0 )
476               {
477                 itsIndex = index;
478                 return;
479               }
480
481             throw Exception("JSON Parsing failed - provided NVP not found");
482           }
483
484         private:
485           MemberIterator itsMemberItBegin, itsMemberItEnd; //!< The member iterator (object)
486           ValueIterator itsValueItBegin, itsValueItEnd;    //!< The value iterator (array)
487           size_t itsIndex;                                 //!< The current index of this iterator
488           enum Type {Value, Member, Null_} itsType;    //!< Whether this holds values (array) or members (objects) or nothing
489       };
490
491       //! Searches for the expectedName node if it doesn't match the actualName
492       /*! This needs to be called before every load or node start occurs.  This function will
493           check to see if an NVP has been provided (with setNextName) and if so, see if that name matches the actual
494           next name given.  If the names do not match, it will search in the current level of the JSON for that name.
495           If the name is not found, an exception will be thrown.
496
497           Resets the NVP name after called.
498
499           @throws Exception if an expectedName is given and not found */
500       inline void search()
501       {
502         // The name an NVP provided with setNextName()
503         if( itsNextName )
504         {
505           // The actual name of the current node
506           auto const actualName = itsIteratorStack.back().name();
507
508           // Do a search if we don't see a name coming up, or if the names don't match
509           if( !actualName || std::strcmp( itsNextName, actualName ) != 0 )
510             itsIteratorStack.back().search( itsNextName );
511         }
512
513         itsNextName = nullptr;
514       }
515
516     public:
517       //! Starts a new node, going into its proper iterator
518       /*! This places an iterator for the next node to be parsed onto the iterator stack.  If the next
519           node is an array, this will be a value iterator, otherwise it will be a member iterator.
520
521           By default our strategy is to start with the document root node and then recursively iterate through
522           all children in the order they show up in the document.
523           We don't need to know NVPs to do this; we'll just blindly load in the order things appear in.
524
525           If we were given an NVP, we will search for it if it does not match our the name of the next node
526           that would normally be loaded.  This functionality is provided by search(). */
527       void startNode()
528       {
529         search();
530
531         if(itsIteratorStack.back().value().IsArray())
532           itsIteratorStack.emplace_back(itsIteratorStack.back().value().Begin(), itsIteratorStack.back().value().End());
533         else
534           itsIteratorStack.emplace_back(itsIteratorStack.back().value().MemberBegin(), itsIteratorStack.back().value().MemberEnd());
535       }
536
537       //! Finishes the most recently started node
538       void finishNode()
539       {
540         itsIteratorStack.pop_back();
541         ++itsIteratorStack.back();
542       }
543
544       //! Sets the name for the next node created with startNode
545       void setNextName( const char * name )
546       {
547         itsNextName = name;
548       }
549
550       //! Loads a value from the current node - small signed overload
551       template<class T> inline
552       typename std::enable_if<std::is_signed<T>::value && sizeof(T) < sizeof(int64_t), void>::type
553       loadValue(T & val)
554       {
555         search();
556
557         val = itsIteratorStack.back().value().GetInt();
558         ++itsIteratorStack.back();
559       }
560
561       //! Loads a value from the current node - small unsigned overload
562       template<class T> inline
563       typename std::enable_if<(std::is_unsigned<T>::value && sizeof(T) < sizeof(uint64_t)) &&
564                               !std::is_same<bool, T>::value, void>::type
565       loadValue(T & val)
566       {
567         search();
568
569         val = itsIteratorStack.back().value().GetUint();
570         ++itsIteratorStack.back();
571       }
572
573       //! Loads a value from the current node - bool overload
574       void loadValue(bool & val)        { search(); val = itsIteratorStack.back().value().GetBool_();   ++itsIteratorStack.back(); }
575       //! Loads a value from the current node - int64 overload
576       void loadValue(int64_t & val)     { search(); val = itsIteratorStack.back().value().GetInt64();  ++itsIteratorStack.back(); }
577       //! Loads a value from the current node - uint64 overload
578       void loadValue(uint64_t & val)    { search(); val = itsIteratorStack.back().value().GetUint64(); ++itsIteratorStack.back(); }
579       //! Loads a value from the current node - float overload
580       void loadValue(float & val)       { search(); val = static_cast<float>(itsIteratorStack.back().value().GetDouble()); ++itsIteratorStack.back(); }
581       //! Loads a value from the current node - double overload
582       void loadValue(double & val)      { search(); val = itsIteratorStack.back().value().GetDouble(); ++itsIteratorStack.back(); }
583       //! Loads a value from the current node - string overload
584       void loadValue(std::string & val) { search(); val = itsIteratorStack.back().value().GetString(); ++itsIteratorStack.back(); }
585
586     private:
587       //! Convert a string to a long long
588       void stringToNumber( std::string const & str, long long & val ) { val = std::stoll( str ); }
589       //! Convert a string to an unsigned long long
590       void stringToNumber( std::string const & str, unsigned long long & val ) { val = std::stoull( str ); }
591       //! Convert a string to a long double
592       void stringToNumber( std::string const & str, long double & val ) { val = std::stold( str ); }
593
594     public:
595       //! Loads a value from the current node - long double and long long overloads
596       template<class T> inline
597       typename std::enable_if<std::is_arithmetic<T>::value &&
598                               !std::is_same<T, long>::value &&
599                               !std::is_same<T, unsigned long>::value &&
600                               !std::is_same<T, std::int64_t>::value &&
601                               !std::is_same<T, std::uint64_t>::value &&
602                               (sizeof(T) >= sizeof(long double) || sizeof(T) >= sizeof(long long)), void>::type
603       loadValue(T & val)
604       {
605         std::string encoded;
606         loadValue( encoded );
607         stringToNumber( encoded, val );
608       }
609
610       //! Loads the size for a SizeTag
611       void loadSize(size_type & size)
612       {
613         size = (itsIteratorStack.rbegin() + 1)->value().Size();
614       }
615
616       //! @}
617
618     private:
619       const char * itsNextName;               //!< Next name set by NVP
620       ReadStream itsReadStream;               //!< Rapidjson write stream
621       std::vector<Iterator> itsIteratorStack; //!< 'Stack' of rapidJSON iterators
622       rapidjson::Document itsDocument;        //!< Rapidjson document
623   };
624
625   // ######################################################################
626   // JSONArchive prologue and epilogue functions
627   // ######################################################################
628
629   // ######################################################################
630   //! Prologue for NVPs for JSON archives
631   /*! NVPs do not start or finish nodes - they just set up the names */
632   template <class T> inline
633   void prologue( JSONOutputArchive &, NameValuePair<T> const & )
634   { }
635
636   //! Prologue for NVPs for JSON archives
637   template <class T> inline
638   void prologue( JSONInputArchive &, NameValuePair<T> const & )
639   { }
640
641   // ######################################################################
642   //! Epilogue for NVPs for JSON archives
643   /*! NVPs do not start or finish nodes - they just set up the names */
644   template <class T> inline
645   void epilogue( JSONOutputArchive &, NameValuePair<T> const & )
646   { }
647
648   //! Epilogue for NVPs for JSON archives
649   /*! NVPs do not start or finish nodes - they just set up the names */
650   template <class T> inline
651   void epilogue( JSONInputArchive &, NameValuePair<T> const & )
652   { }
653
654   // ######################################################################
655   //! Prologue for SizeTags for JSON archives
656   /*! SizeTags are strictly ignored for JSON, they just indicate
657       that the current node should be made into an array */
658   template <class T> inline
659   void prologue( JSONOutputArchive & ar, SizeTag<T> const & )
660   {
661     ar.makeArray();
662   }
663
664   //! Prologue for SizeTags for JSON archives
665   template <class T> inline
666   void prologue( JSONInputArchive &, SizeTag<T> const & )
667   { }
668
669   // ######################################################################
670   //! Epilogue for SizeTags for JSON archives
671   /*! SizeTags are strictly ignored for JSON */
672   template <class T> inline
673   void epilogue( JSONOutputArchive &, SizeTag<T> const & )
674   { }
675
676   //! Epilogue for SizeTags for JSON archives
677   template <class T> inline
678   void epilogue( JSONInputArchive &, SizeTag<T> const & )
679   { }
680
681   // ######################################################################
682   //! Prologue for all other types for JSON archives (except minimal types)
683   /*! Starts a new node, named either automatically or by some NVP,
684       that may be given data by the type about to be archived
685
686       Minimal types do not start or finish nodes */
687   template <class T> inline
688   typename std::enable_if<!std::is_arithmetic<T>::value &&
689                           !traits::has_minimal_output_serialization<T, JSONOutputArchive>::value, void>::type
690   prologue( JSONOutputArchive & ar, T const & )
691   {
692     ar.startNode();
693   }
694
695   //! Prologue for all other types for JSON archives
696   template <class T> inline
697   typename std::enable_if<!std::is_arithmetic<T>::value &&
698                           !traits::has_minimal_input_serialization<T, JSONOutputArchive>::value, void>::type
699   prologue( JSONInputArchive & ar, T const & )
700   {
701     ar.startNode();
702   }
703
704   // ######################################################################
705   //! Epilogue for all other types other for JSON archives (except minimal types
706   /*! Finishes the node created in the prologue
707
708       Minimal types do not start or finish nodes */
709   template <class T> inline
710   typename std::enable_if<!std::is_arithmetic<T>::value &&
711                           !traits::has_minimal_output_serialization<T, JSONOutputArchive>::value, void>::type
712   epilogue( JSONOutputArchive & ar, T const & )
713   {
714     ar.finishNode();
715   }
716
717   //! Epilogue for all other types other for JSON archives
718   template <class T> inline
719   typename std::enable_if<!std::is_arithmetic<T>::value &&
720                           !traits::has_minimal_input_serialization<T, JSONOutputArchive>::value, void>::type
721   epilogue( JSONInputArchive & ar, T const & )
722   {
723     ar.finishNode();
724   }
725
726   // ######################################################################
727   //! Prologue for arithmetic types for JSON archives
728   template <class T> inline
729   typename std::enable_if<std::is_arithmetic<T>::value, void>::type
730   prologue( JSONOutputArchive & ar, T const & )
731   {
732     ar.writeName();
733   }
734
735   //! Prologue for arithmetic types for JSON archives
736   template <class T> inline
737   typename std::enable_if<std::is_arithmetic<T>::value, void>::type
738   prologue( JSONInputArchive &, T const & )
739   { }
740
741   // ######################################################################
742   //! Epilogue for arithmetic types for JSON archives
743   template <class T> inline
744   typename std::enable_if<std::is_arithmetic<T>::value, void>::type
745   epilogue( JSONOutputArchive &, T const & )
746   { }
747
748   //! Epilogue for arithmetic types for JSON archives
749   template <class T> inline
750   typename std::enable_if<std::is_arithmetic<T>::value, void>::type
751   epilogue( JSONInputArchive &, T const & )
752   { }
753
754   // ######################################################################
755   //! Prologue for strings for JSON archives
756   template<class CharT, class Traits, class Alloc> inline
757   void prologue(JSONOutputArchive & ar, std::basic_string<CharT, Traits, Alloc> const &)
758   {
759     ar.writeName();
760   }
761
762   //! Prologue for strings for JSON archives
763   template<class CharT, class Traits, class Alloc> inline
764   void prologue(JSONInputArchive &, std::basic_string<CharT, Traits, Alloc> const &)
765   { }
766
767   // ######################################################################
768   //! Epilogue for strings for JSON archives
769   template<class CharT, class Traits, class Alloc> inline
770   void epilogue(JSONOutputArchive &, std::basic_string<CharT, Traits, Alloc> const &)
771   { }
772
773   //! Epilogue for strings for JSON archives
774   template<class CharT, class Traits, class Alloc> inline
775   void epilogue(JSONInputArchive &, std::basic_string<CharT, Traits, Alloc> const &)
776   { }
777
778   // ######################################################################
779   // Common JSONArchive serialization functions
780   // ######################################################################
781
782   //! Serializing NVP types to JSON
783   template <class T> inline
784   void save( JSONOutputArchive & ar, NameValuePair<T> const & t )
785   {
786     ar.setNextName( t.name );
787     ar( t.value );
788   }
789
790   template <class T> inline
791   void load( JSONInputArchive & ar, NameValuePair<T> & t )
792   {
793     ar.setNextName( t.name );
794     ar( t.value );
795   }
796
797   //! Saving for arithmetic to JSON
798   template<class T> inline
799   typename std::enable_if<std::is_arithmetic<T>::value, void>::type
800   save(JSONOutputArchive & ar, T const & t)
801   {
802     ar.saveValue( t );
803   }
804
805   //! Loading arithmetic from JSON
806   template<class T> inline
807   typename std::enable_if<std::is_arithmetic<T>::value, void>::type
808   load(JSONInputArchive & ar, T & t)
809   {
810     ar.loadValue( t );
811   }
812
813   //! saving string to JSON
814   template<class CharT, class Traits, class Alloc> inline
815   void save(JSONOutputArchive & ar, std::basic_string<CharT, Traits, Alloc> const & str)
816   {
817     ar.saveValue( str );
818   }
819
820   //! loading string from JSON
821   template<class CharT, class Traits, class Alloc> inline
822   void load(JSONInputArchive & ar, std::basic_string<CharT, Traits, Alloc> & str)
823   {
824     ar.loadValue( str );
825   }
826
827   // ######################################################################
828   //! Saving SizeTags to JSON
829   template <class T> inline
830   void save( JSONOutputArchive &, SizeTag<T> const & )
831   {
832     // nothing to do here, we don't explicitly save the size
833   }
834
835   //! Loading SizeTags from JSON
836   template <class T> inline
837   void load( JSONInputArchive & ar, SizeTag<T> & st )
838   {
839     ar.loadSize( st.size );
840   }
841 } // namespace cereal
842
843 // register archives for polymorphic support
844 CEREAL_REGISTER_ARCHIVE(cereal::JSONInputArchive)
845 CEREAL_REGISTER_ARCHIVE(cereal::JSONOutputArchive)
846
847 #endif // CEREAL_ARCHIVES_JSON_HPP_