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