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