LCOV - code coverage report
Current view: top level - cereal/archives - json.hpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 221 229 96.5 %
Date: 2017-02-12 13:57:59 Functions: 1433 1484 96.6 %

          Line data    Source code
       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           0 :   struct RapidJSONException : Exception
      40           0 :   { 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
     112        3502 :           static Options Default(){ return Options(); }
     113             : 
     114             :           //! Default options with no indentation
     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) */
     131        3502 :           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        3502 :             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 */
     149        3502 :       JSONOutputArchive(std::ostream & stream, Options const & options = Options::Default() ) :
     150             :         OutputArchive<JSONOutputArchive>(this),
     151             :         itsWriteStream(stream),
     152             :         itsWriter(itsWriteStream),
     153        3502 :         itsNextName(nullptr)
     154             :       {
     155        3502 :         itsWriter.SetMaxDecimalPlaces( options.itsPrecision );
     156        3502 :         itsWriter.SetIndent( options.itsIndentChar, options.itsIndentLength );
     157        3502 :         itsNameCounter.push(0);
     158        3502 :         itsNodeStack.push(NodeType::StartObject);
     159        3502 :       }
     160             : 
     161             :       //! Destructor, flushes the JSON
     162        3502 :       ~JSONOutputArchive() CEREAL_NOEXCEPT
     163        7004 :       {
     164        3502 :         if (itsNodeStack.top() == NodeType::InObject)
     165        3502 :           itsWriter.EndObject();
     166           0 :         else if (itsNodeStack.top() == NodeType::InArray)
     167           0 :           itsWriter.EndArray();
     168        3502 :       }
     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 */
     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 */
     193     1321748 :       void startNode()
     194             :       {
     195     1321748 :         writeName();
     196     1321748 :         itsNodeStack.push(NodeType::StartObject);
     197     1321748 :         itsNameCounter.push(0);
     198     1321748 :       }
     199             : 
     200             :       //! Designates the most recently added node as finished
     201     1321748 :       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     1321748 :         switch(itsNodeStack.top())
     209             :         {
     210             :           case NodeType::StartArray:
     211         100 :             itsWriter.StartArray();
     212             :           case NodeType::InArray:
     213        9700 :             itsWriter.EndArray();
     214        9700 :             break;
     215             :           case NodeType::StartObject:
     216         700 :             itsWriter.StartObject();
     217             :           case NodeType::InObject:
     218     1312048 :             itsWriter.EndObject();
     219     1312048 :             break;
     220             :         }
     221             : 
     222     1321748 :         itsNodeStack.pop();
     223     1321748 :         itsNameCounter.pop();
     224     1321748 :       }
     225             : 
     226             :       //! Sets the name for the next node created with startNode
     227      715151 :       void setNextName( const char * name )
     228             :       {
     229      715151 :         itsNextName = name;
     230      715151 :       }
     231             : 
     232             :       //! Saves a bool to the current node
     233       11100 :       void saveValue(bool b)                { itsWriter.Bool(b);                                                         }
     234             :       //! Saves an int to the current node
     235     2323018 :       void saveValue(int i)                 { itsWriter.Int(i);                                                          }
     236             :       //! Saves a uint to the current node
     237       74601 :       void saveValue(unsigned u)            { itsWriter.Uint(u);                                                         }
     238             :       //! Saves an int64 to the current node
     239        1000 :       void saveValue(int64_t i64)           { itsWriter.Int64(i64);                                                      }
     240             :       //! Saves a uint64 to the current node
     241        1450 :       void saveValue(uint64_t u64)          { itsWriter.Uint64(u64);                                                     }
     242             :       //! Saves a double to the current node
     243        1701 :       void saveValue(double d)              { itsWriter.Double(d);                                                       }
     244             :       //! Saves a string to the current node
     245     2015198 :       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
     247      715151 :       void saveValue(char const * s)        { itsWriter.String(s);                                                       }
     248             :       //! Saves a nullptr to the current node
     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
     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
     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
     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
     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
     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
     301         500 :       void saveValue(T const & t)
     302             :       {
     303        1000 :         std::stringstream ss; ss.precision( std::numeric_limits<long double>::max_digits10 );
     304         500 :         ss << t;
     305         500 :         saveValue( ss.str() );
     306         500 :       }
     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             :           */
     321     3796569 :       void writeName()
     322             :       {
     323     3796569 :         NodeType const & nodeType = itsNodeStack.top();
     324             : 
     325             :         // Start up either an object or an array, depending on state
     326     3796569 :         if(nodeType == NodeType::StartArray)
     327             :         {
     328        9600 :           itsWriter.StartArray();
     329        9600 :           itsNodeStack.top() = NodeType::InArray;
     330             :         }
     331     3786969 :         else if(nodeType == NodeType::StartObject)
     332             :         {
     333     1314850 :           itsNodeStack.top() = NodeType::InObject;
     334     1314850 :           itsWriter.StartObject();
     335             :         }
     336             : 
     337             :         // Array types do not output names
     338     3796569 :         if(nodeType == NodeType::InArray) return;
     339             : 
     340     2668398 :         if(itsNextName == nullptr)
     341             :         {
     342     3906494 :           std::string name = "value" + std::to_string( itsNameCounter.top()++ ) + "\0";
     343     1953247 :           saveValue(name);
     344             :         }
     345             :         else
     346             :         {
     347      715151 :           saveValue(itsNextName);
     348      715151 :           itsNextName = nullptr;
     349             :         }
     350             :       }
     351             : 
     352             :       //! Designates that the current node should be output as an array, not an object
     353        9700 :       void makeArray()
     354             :       {
     355        9700 :         itsNodeStack.top() = NodeType::StartArray;
     356        9700 :       }
     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 */
     422        3602 :       JSONInputArchive(std::istream & stream) :
     423             :         InputArchive<JSONInputArchive>(this),
     424             :         itsNextName( nullptr ),
     425        3602 :         itsReadStream(stream)
     426             :       {
     427        3602 :         itsDocument.ParseStream<>(itsReadStream);
     428        3602 :         if (itsDocument.IsArray())
     429           0 :           itsIteratorStack.emplace_back(itsDocument.Begin(), itsDocument.End());
     430             :         else
     431        3602 :           itsIteratorStack.emplace_back(itsDocument.MemberBegin(), itsDocument.MemberEnd());
     432        3602 :       }
     433             : 
     434        3602 :       ~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 */
     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:
     470             :           Iterator() : itsIndex( 0 ), itsType(Null_) {}
     471             : 
     472     1315950 :           Iterator(MemberIterator begin, MemberIterator end) :
     473     1315950 :             itsMemberItBegin(begin), itsMemberItEnd(end), itsIndex(0), itsType(Member)
     474             :           {
     475     1315950 :             if( std::distance( begin, end ) == 0 )
     476         700 :               itsType = Null_;
     477     1315950 :           }
     478             : 
     479        9700 :           Iterator(ValueIterator begin, ValueIterator end) :
     480        9700 :             itsValueItBegin(begin), itsValueItEnd(end), itsIndex(0), itsType(Value)
     481             :           {
     482        9700 :             if( std::distance( begin, end ) == 0 )
     483         100 :               itsType = Null_;
     484        9700 :           }
     485             : 
     486             :           //! Advance to the next node
     487     3796769 :           Iterator & operator++()
     488             :           {
     489     3796769 :             ++itsIndex;
     490     3796769 :             return *this;
     491             :           }
     492             : 
     493             :           //! Get the value of the current node
     494     6450865 :           GenericValue const & value()
     495             :           {
     496     6450865 :             switch(itsType)
     497             :             {
     498     3083513 :               case Value : return itsValueItBegin[itsIndex];
     499     3367352 :               case Member: return itsMemberItBegin[itsIndex].value;
     500           0 :               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
     505      715251 :           const char * name() const
     506             :           {
     507      715251 :             if( itsType == Member && (itsMemberItBegin + itsIndex) != itsMemberItEnd )
     508      715151 :               return itsMemberItBegin[itsIndex].name.GetString();
     509             :             else
     510         100 :               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 */
     515         900 :           inline void search( const char * searchName )
     516             :           {
     517         900 :             const auto len = std::strlen( searchName );
     518         900 :             size_t index = 0;
     519        3100 :             for( auto it = itsMemberItBegin; it != itsMemberItEnd; ++it, ++index )
     520             :             {
     521        3100 :               const auto currentName = it->name.GetString();
     522        4000 :               if( ( std::strncmp( searchName, currentName, len ) == 0 ) &&
     523         900 :                   ( std::strlen( currentName ) == len ) )
     524             :               {
     525         900 :                 itsIndex = index;
     526        2700 :                 return;
     527             :               }
     528             :             }
     529             : 
     530           0 :             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 */
     549     3797069 :       inline void search()
     550             :       {
     551             :         // The name an NVP provided with setNextName()
     552     3797069 :         if( itsNextName )
     553             :         {
     554             :           // The actual name of the current node
     555      715251 :           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      715251 :           if( !actualName || std::strcmp( itsNextName, actualName ) != 0 )
     559         900 :             itsIteratorStack.back().search( itsNextName );
     560             :         }
     561             : 
     562     3797069 :         itsNextName = nullptr;
     563     3797069 :       }
     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(). */
     576     1322048 :       void startNode()
     577             :       {
     578     1322048 :         search();
     579             : 
     580     1322048 :         if(itsIteratorStack.back().value().IsArray())
     581        9700 :           itsIteratorStack.emplace_back(itsIteratorStack.back().value().Begin(), itsIteratorStack.back().value().End());
     582             :         else
     583     1312348 :           itsIteratorStack.emplace_back(itsIteratorStack.back().value().MemberBegin(), itsIteratorStack.back().value().MemberEnd());
     584     1322048 :       }
     585             : 
     586             :       //! Finishes the most recently started node
     587     1321748 :       void finishNode()
     588             :       {
     589     1321748 :         itsIteratorStack.pop_back();
     590     1321748 :         ++itsIteratorStack.back();
     591     1321748 :       }
     592             : 
     593             :       //! Retrieves the current node name
     594             :       /*! @return nullptr if no name exists */
     595             :       const char * getNodeName() const
     596             :       {
     597             :         return itsIteratorStack.back().name();
     598             :       }
     599             : 
     600             :       //! Sets the name for the next node created with startNode
     601      715251 :       void setNextName( const char * name )
     602             :       {
     603      715251 :         itsNextName = name;
     604      715251 :       }
     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
     609     2271625 :       void loadValue(T & val)
     610             :       {
     611     2271625 :         search();
     612             : 
     613     2271625 :         val = static_cast<T>( itsIteratorStack.back().value().GetInt() );
     614     2271625 :         ++itsIteratorStack.back();
     615     2271625 :       }
     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
     621      126194 :       void loadValue(T & val)
     622             :       {
     623      126194 :         search();
     624             : 
     625      126194 :         val = static_cast<T>( itsIteratorStack.back().value().GetUint() );
     626      126194 :         ++itsIteratorStack.back();
     627      126194 :       }
     628             : 
     629             :       //! Loads a value from the current node - bool overload
     630       11100 :       void loadValue(bool & val)        { search(); val = itsIteratorStack.back().value().GetBool(); ++itsIteratorStack.back(); }
     631             :       //! Loads a value from the current node - int64 overload
     632        1000 :       void loadValue(int64_t & val)     { search(); val = itsIteratorStack.back().value().GetInt64(); ++itsIteratorStack.back(); }
     633             :       //! Loads a value from the current node - uint64 overload
     634        1450 :       void loadValue(uint64_t & val)    { search(); val = itsIteratorStack.back().value().GetUint64(); ++itsIteratorStack.back(); }
     635             :       //! Loads a value from the current node - float overload
     636         600 :       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
     638        1101 :       void loadValue(double & val)      { search(); val = itsIteratorStack.back().value().GetDouble(); ++itsIteratorStack.back(); }
     639             :       //! Loads a value from the current node - string overload
     640       61951 :       void loadValue(std::string & val) { search(); val = itsIteratorStack.back().value().GetString(); ++itsIteratorStack.back(); }
     641             :       //! Loads a nullptr from the current node
     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
     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
     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
     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
     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
     686         100 :       void stringToNumber( std::string const & str, long long & val ) { val = std::stoll( str ); }
     687             :       //! Convert a string to an unsigned long long
     688         100 :       void stringToNumber( std::string const & str, unsigned long long & val ) { val = std::stoull( str ); }
     689             :       //! Convert a string to a long double
     690         300 :       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>
     700         500 :       inline void loadValue(T & val)
     701             :       {
     702        1000 :         std::string encoded;
     703         500 :         loadValue( encoded );
     704         500 :         stringToNumber( encoded, val );
     705         500 :       }
     706             : 
     707             :       //! Loads the size for a SizeTag
     708        9700 :       void loadSize(size_type & size)
     709             :       {
     710        9700 :         if (itsIteratorStack.size() == 1)
     711           0 :           size = itsDocument.Size();
     712             :         else
     713        9700 :           size = (itsIteratorStack.rbegin() + 1)->value().Size();
     714        9700 :       }
     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
     733      715151 :   void prologue( JSONOutputArchive &, NameValuePair<T> const & )
     734      715151 :   { }
     735             : 
     736             :   //! Prologue for NVPs for JSON archives
     737             :   template <class T> inline
     738      715251 :   void prologue( JSONInputArchive &, NameValuePair<T> const & )
     739      715251 :   { }
     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
     745      715151 :   void epilogue( JSONOutputArchive &, NameValuePair<T> const & )
     746      715151 :   { }
     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
     751      715051 :   void epilogue( JSONInputArchive &, NameValuePair<T> const & )
     752      715051 :   { }
     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
     759        9700 :   void prologue( JSONOutputArchive & ar, SizeTag<T> const & )
     760             :   {
     761        9700 :     ar.makeArray();
     762        9700 :   }
     763             : 
     764             :   //! Prologue for SizeTags for JSON archives
     765             :   template <class T> inline
     766        9700 :   void prologue( JSONInputArchive &, SizeTag<T> const & )
     767        9700 :   { }
     768             : 
     769             :   // ######################################################################
     770             :   //! Epilogue for SizeTags for JSON archives
     771             :   /*! SizeTags are strictly ignored for JSON */
     772             :   template <class T> inline
     773        9700 :   void epilogue( JSONOutputArchive &, SizeTag<T> const & )
     774        9700 :   { }
     775             : 
     776             :   //! Epilogue for SizeTags for JSON archives
     777             :   template <class T> inline
     778        9700 :   void epilogue( JSONInputArchive &, SizeTag<T> const & )
     779        9700 :   { }
     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>
     790     1321748 :   inline void prologue( JSONOutputArchive & ar, T const & )
     791             :   {
     792     1321748 :     ar.startNode();
     793     1321748 :   }
     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>
     799     1322048 :   inline void prologue( JSONInputArchive & ar, T const & )
     800             :   {
     801     1322048 :     ar.startNode();
     802     1322048 :   }
     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>
     812     1321748 :   inline void epilogue( JSONOutputArchive & ar, T const & )
     813             :   {
     814     1321748 :     ar.finishNode();
     815     1321748 :   }
     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>
     821     1321748 :   inline void epilogue( JSONInputArchive & ar, T const & )
     822             :   {
     823     1321748 :     ar.finishNode();
     824     1321748 :   }
     825             : 
     826             :   // ######################################################################
     827             :   //! Prologue for arithmetic types for JSON archives
     828             :   inline
     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
     836             :   void prologue( JSONInputArchive &, std::nullptr_t const & )
     837             :   { }
     838             : 
     839             :   // ######################################################################
     840             :   //! Epilogue for arithmetic types for JSON archives
     841             :   inline
     842             :   void epilogue( JSONOutputArchive &, std::nullptr_t const & )
     843             :   { }
     844             : 
     845             :   //! Epilogue for arithmetic types for JSON archives
     846             :   inline
     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
     853     2413370 :   void prologue( JSONOutputArchive & ar, T const & )
     854             :   {
     855     2413370 :     ar.writeName();
     856     2413370 :   }
     857             : 
     858             :   //! Prologue for arithmetic types for JSON archives
     859             :   template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
     860     2413570 :   void prologue( JSONInputArchive &, T const & )
     861     2413570 :   { }
     862             : 
     863             :   // ######################################################################
     864             :   //! Epilogue for arithmetic types for JSON archives
     865             :   template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
     866     2413370 :   void epilogue( JSONOutputArchive &, T const & )
     867     2413370 :   { }
     868             : 
     869             :   //! Epilogue for arithmetic types for JSON archives
     870             :   template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
     871     2413570 :   void epilogue( JSONInputArchive &, T const & )
     872     2413570 :   { }
     873             : 
     874             :   // ######################################################################
     875             :   //! Prologue for strings for JSON archives
     876             :   template<class CharT, class Traits, class Alloc> inline
     877       61451 :   void prologue(JSONOutputArchive & ar, std::basic_string<CharT, Traits, Alloc> const &)
     878             :   {
     879       61451 :     ar.writeName();
     880       61451 :   }
     881             : 
     882             :   //! Prologue for strings for JSON archives
     883             :   template<class CharT, class Traits, class Alloc> inline
     884       61451 :   void prologue(JSONInputArchive &, std::basic_string<CharT, Traits, Alloc> const &)
     885       61451 :   { }
     886             : 
     887             :   // ######################################################################
     888             :   //! Epilogue for strings for JSON archives
     889             :   template<class CharT, class Traits, class Alloc> inline
     890       61451 :   void epilogue(JSONOutputArchive &, std::basic_string<CharT, Traits, Alloc> const &)
     891       61451 :   { }
     892             : 
     893             :   //! Epilogue for strings for JSON archives
     894             :   template<class CharT, class Traits, class Alloc> inline
     895       61451 :   void epilogue(JSONInputArchive &, std::basic_string<CharT, Traits, Alloc> const &)
     896       61451 :   { }
     897             : 
     898             :   // ######################################################################
     899             :   // Common JSONArchive serialization functions
     900             :   // ######################################################################
     901             :   //! Serializing NVP types to JSON
     902             :   template <class T> inline
     903      715151 :   void CEREAL_SAVE_FUNCTION_NAME( JSONOutputArchive & ar, NameValuePair<T> const & t )
     904             :   {
     905      715151 :     ar.setNextName( t.name );
     906      715151 :     ar( t.value );
     907      715151 :   }
     908             : 
     909             :   template <class T> inline
     910      715251 :   void CEREAL_LOAD_FUNCTION_NAME( JSONInputArchive & ar, NameValuePair<T> & t )
     911             :   {
     912      715251 :     ar.setNextName( t.name );
     913      715251 :     ar( t.value );
     914      715051 :   }
     915             : 
     916             :   //! Saving for nullptr to JSON
     917             :   inline
     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
     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
     932     2413370 :   void CEREAL_SAVE_FUNCTION_NAME(JSONOutputArchive & ar, T const & t)
     933             :   {
     934     2413370 :     ar.saveValue( t );
     935     2413370 :   }
     936             : 
     937             :   //! Loading arithmetic from JSON
     938             :   template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae> inline
     939     2413570 :   void CEREAL_LOAD_FUNCTION_NAME(JSONInputArchive & ar, T & t)
     940             :   {
     941     2413570 :     ar.loadValue( t );
     942     2413570 :   }
     943             : 
     944             :   //! saving string to JSON
     945             :   template<class CharT, class Traits, class Alloc> inline
     946       61451 :   void CEREAL_SAVE_FUNCTION_NAME(JSONOutputArchive & ar, std::basic_string<CharT, Traits, Alloc> const & str)
     947             :   {
     948       61451 :     ar.saveValue( str );
     949       61451 :   }
     950             : 
     951             :   //! loading string from JSON
     952             :   template<class CharT, class Traits, class Alloc> inline
     953       61451 :   void CEREAL_LOAD_FUNCTION_NAME(JSONInputArchive & ar, std::basic_string<CharT, Traits, Alloc> & str)
     954             :   {
     955       61451 :     ar.loadValue( str );
     956       61451 :   }
     957             : 
     958             :   // ######################################################################
     959             :   //! Saving SizeTags to JSON
     960             :   template <class T> inline
     961        9700 :   void CEREAL_SAVE_FUNCTION_NAME( JSONOutputArchive &, SizeTag<T> const & )
     962             :   {
     963             :     // nothing to do here, we don't explicitly save the size
     964        9700 :   }
     965             : 
     966             :   //! Loading SizeTags from JSON
     967             :   template <class T> inline
     968        9700 :   void CEREAL_LOAD_FUNCTION_NAME( JSONInputArchive & ar, SizeTag<T> & st )
     969             :   {
     970        9700 :     ar.loadSize( st.size );
     971        9700 :   }
     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_

Generated by: LCOV version 1.11