1 // __ _____ _____ _____ 2 // __| | __| | | | JSON for Modern C++ 3 // | | |__ | | | | | | version 3.11.2 4 // |_____|_____|_____|_|___| https://github.com/nlohmann/json 5 // 6 // SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me> 7 // SPDX-License-Identifier: MIT 8 9 #pragma once 10 11 #include <algorithm> // all_of 12 #include <cctype> // isdigit 13 #include <cerrno> // errno, ERANGE 14 #include <cstdlib> // strtoull 15 #ifndef JSON_NO_IO 16 #include <iosfwd> // ostream 17 #endif // JSON_NO_IO 18 #include <limits> // max 19 #include <numeric> // accumulate 20 #include <string> // string 21 #include <utility> // move 22 #include <vector> // vector 23 24 #include <nlohmann/detail/exceptions.hpp> 25 #include <nlohmann/detail/macro_scope.hpp> 26 #include <nlohmann/detail/string_concat.hpp> 27 #include <nlohmann/detail/string_escape.hpp> 28 #include <nlohmann/detail/value_t.hpp> 29 30 NLOHMANN_JSON_NAMESPACE_BEGIN 31 32 /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document 33 /// @sa https://json.nlohmann.me/api/json_pointer/ 34 template<typename RefStringType> 35 class json_pointer 36 { 37 // allow basic_json to access private members 38 NLOHMANN_BASIC_JSON_TPL_DECLARATION 39 friend class basic_json; 40 41 template<typename> 42 friend class json_pointer; 43 44 template<typename T> 45 struct string_t_helper 46 { 47 using type = T; 48 }; 49 50 NLOHMANN_BASIC_JSON_TPL_DECLARATION 51 struct string_t_helper<NLOHMANN_BASIC_JSON_TPL> 52 { 53 using type = StringType; 54 }; 55 56 public: 57 // for backwards compatibility accept BasicJsonType 58 using string_t = typename string_t_helper<RefStringType>::type; 59 60 /// @brief create JSON pointer 61 /// @sa https://json.nlohmann.me/api/json_pointer/json_pointer/ json_pointer(const string_t& s = �)62 explicit json_pointer(const string_t& s = "") 63 : reference_tokens(split(s)) 64 {} 65 66 /// @brief return a string representation of the JSON pointer 67 /// @sa https://json.nlohmann.me/api/json_pointer/to_string/ to_string() const68 string_t to_string() const 69 { 70 return std::accumulate(reference_tokens.begin(), reference_tokens.end(), 71 string_t{}, 72 [](const string_t& a, const string_t& b) 73 { 74 return detail::concat(a, '/', detail::escape(b)); 75 }); 76 } 77 78 /// @brief return a string representation of the JSON pointer 79 /// @sa https://json.nlohmann.me/api/json_pointer/operator_string/ 80 JSON_HEDLEY_DEPRECATED_FOR(3.11.0, to_string()) operator string_t() const81 operator string_t() const 82 { 83 return to_string(); 84 } 85 86 #ifndef JSON_NO_IO 87 /// @brief write string representation of the JSON pointer to stream 88 /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/ operator <<(std::ostream& o, const json_pointer& ptr)89 friend std::ostream& operator<<(std::ostream& o, const json_pointer& ptr) 90 { 91 o << ptr.to_string(); 92 return o; 93 } 94 #endif 95 96 /// @brief append another JSON pointer at the end of this JSON pointer 97 /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/ operator /=(const json_pointer& ptr)98 json_pointer& operator/=(const json_pointer& ptr) 99 { 100 reference_tokens.insert(reference_tokens.end(), 101 ptr.reference_tokens.begin(), 102 ptr.reference_tokens.end()); 103 return *this; 104 } 105 106 /// @brief append an unescaped reference token at the end of this JSON pointer 107 /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/ operator /=(string_t token)108 json_pointer& operator/=(string_t token) 109 { 110 push_back(std::move(token)); 111 return *this; 112 } 113 114 /// @brief append an array index at the end of this JSON pointer 115 /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/ operator /=(std::size_t array_idx)116 json_pointer& operator/=(std::size_t array_idx) 117 { 118 return *this /= std::to_string(array_idx); 119 } 120 121 /// @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer 122 /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ operator /(const json_pointer& lhs, const json_pointer& rhs)123 friend json_pointer operator/(const json_pointer& lhs, 124 const json_pointer& rhs) 125 { 126 return json_pointer(lhs) /= rhs; 127 } 128 129 /// @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer 130 /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ operator /(const json_pointer& lhs, string_t token)131 friend json_pointer operator/(const json_pointer& lhs, string_t token) // NOLINT(performance-unnecessary-value-param) 132 { 133 return json_pointer(lhs) /= std::move(token); 134 } 135 136 /// @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer 137 /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ operator /(const json_pointer& lhs, std::size_t array_idx)138 friend json_pointer operator/(const json_pointer& lhs, std::size_t array_idx) 139 { 140 return json_pointer(lhs) /= array_idx; 141 } 142 143 /// @brief returns the parent of this JSON pointer 144 /// @sa https://json.nlohmann.me/api/json_pointer/parent_pointer/ parent_pointer() const145 json_pointer parent_pointer() const 146 { 147 if (empty()) 148 { 149 return *this; 150 } 151 152 json_pointer res = *this; 153 res.pop_back(); 154 return res; 155 } 156 157 /// @brief remove last reference token 158 /// @sa https://json.nlohmann.me/api/json_pointer/pop_back/ pop_back()159 void pop_back() 160 { 161 if (JSON_HEDLEY_UNLIKELY(empty())) 162 { 163 JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr)); 164 } 165 166 reference_tokens.pop_back(); 167 } 168 169 /// @brief return last reference token 170 /// @sa https://json.nlohmann.me/api/json_pointer/back/ back() const171 const string_t& back() const 172 { 173 if (JSON_HEDLEY_UNLIKELY(empty())) 174 { 175 JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr)); 176 } 177 178 return reference_tokens.back(); 179 } 180 181 /// @brief append an unescaped token at the end of the reference pointer 182 /// @sa https://json.nlohmann.me/api/json_pointer/push_back/ push_back(const string_t& token)183 void push_back(const string_t& token) 184 { 185 reference_tokens.push_back(token); 186 } 187 188 /// @brief append an unescaped token at the end of the reference pointer 189 /// @sa https://json.nlohmann.me/api/json_pointer/push_back/ push_back(string_t&& token)190 void push_back(string_t&& token) 191 { 192 reference_tokens.push_back(std::move(token)); 193 } 194 195 /// @brief return whether pointer points to the root document 196 /// @sa https://json.nlohmann.me/api/json_pointer/empty/ 197 bool empty() const noexcept 198 { 199 return reference_tokens.empty(); 200 } 201 202 private: 203 /*! 204 @param[in] s reference token to be converted into an array index 205 206 @return integer representation of @a s 207 208 @throw parse_error.106 if an array index begins with '0' 209 @throw parse_error.109 if an array index begins not with a digit 210 @throw out_of_range.404 if string @a s could not be converted to an integer 211 @throw out_of_range.410 if an array index exceeds size_type 212 */ 213 template<typename BasicJsonType> array_index(const string_t& s)214 static typename BasicJsonType::size_type array_index(const string_t& s) 215 { 216 using size_type = typename BasicJsonType::size_type; 217 218 // error condition (cf. RFC 6901, Sect. 4) 219 if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0')) 220 { 221 JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr)); 222 } 223 224 // error condition (cf. RFC 6901, Sect. 4) 225 if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9'))) 226 { 227 JSON_THROW(detail::parse_error::create(109, 0, detail::concat("array index '", s, "' is not a number"), nullptr)); 228 } 229 230 const char* p = s.c_str(); 231 char* p_end = nullptr; 232 errno = 0; // strtoull doesn't reset errno 233 unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int) 234 if (p == p_end // invalid input or empty string 235 || errno == ERANGE // out of range 236 || JSON_HEDLEY_UNLIKELY(static_cast<std::size_t>(p_end - p) != s.size())) // incomplete read 237 { 238 JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", s, "'"), nullptr)); 239 } 240 241 // only triggered on special platforms (like 32bit), see also 242 // https://github.com/nlohmann/json/pull/2203 243 if (res >= static_cast<unsigned long long>((std::numeric_limits<size_type>::max)())) // NOLINT(runtime/int) 244 { 245 JSON_THROW(detail::out_of_range::create(410, detail::concat("array index ", s, " exceeds size_type"), nullptr)); // LCOV_EXCL_LINE 246 } 247 248 return static_cast<size_type>(res); 249 } 250 251 JSON_PRIVATE_UNLESS_TESTED: top() const252 json_pointer top() const 253 { 254 if (JSON_HEDLEY_UNLIKELY(empty())) 255 { 256 JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr)); 257 } 258 259 json_pointer result = *this; 260 result.reference_tokens = {reference_tokens[0]}; 261 return result; 262 } 263 264 private: 265 /*! 266 @brief create and return a reference to the pointed to value 267 268 @complexity Linear in the number of reference tokens. 269 270 @throw parse_error.109 if array index is not a number 271 @throw type_error.313 if value cannot be unflattened 272 */ 273 template<typename BasicJsonType> get_and_create(BasicJsonType& j) const274 BasicJsonType& get_and_create(BasicJsonType& j) const 275 { 276 auto* result = &j; 277 278 // in case no reference tokens exist, return a reference to the JSON value 279 // j which will be overwritten by a primitive value 280 for (const auto& reference_token : reference_tokens) 281 { 282 switch (result->type()) 283 { 284 case detail::value_t::null: 285 { 286 if (reference_token == "0") 287 { 288 // start a new array if reference token is 0 289 result = &result->operator[](0); 290 } 291 else 292 { 293 // start a new object otherwise 294 result = &result->operator[](reference_token); 295 } 296 break; 297 } 298 299 case detail::value_t::object: 300 { 301 // create an entry in the object 302 result = &result->operator[](reference_token); 303 break; 304 } 305 306 case detail::value_t::array: 307 { 308 // create an entry in the array 309 result = &result->operator[](array_index<BasicJsonType>(reference_token)); 310 break; 311 } 312 313 /* 314 The following code is only reached if there exists a reference 315 token _and_ the current value is primitive. In this case, we have 316 an error situation, because primitive values may only occur as 317 single value; that is, with an empty list of reference tokens. 318 */ 319 case detail::value_t::string: 320 case detail::value_t::boolean: 321 case detail::value_t::number_integer: 322 case detail::value_t::number_unsigned: 323 case detail::value_t::number_float: 324 case detail::value_t::binary: 325 case detail::value_t::discarded: 326 default: 327 JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", &j)); 328 } 329 } 330 331 return *result; 332 } 333 334 /*! 335 @brief return a reference to the pointed to value 336 337 @note This version does not throw if a value is not present, but tries to 338 create nested values instead. For instance, calling this function 339 with pointer `"/this/that"` on a null value is equivalent to calling 340 `operator[]("this").operator[]("that")` on that value, effectively 341 changing the null value to an object. 342 343 @param[in] ptr a JSON value 344 345 @return reference to the JSON value pointed to by the JSON pointer 346 347 @complexity Linear in the length of the JSON pointer. 348 349 @throw parse_error.106 if an array index begins with '0' 350 @throw parse_error.109 if an array index was not a number 351 @throw out_of_range.404 if the JSON pointer can not be resolved 352 */ 353 template<typename BasicJsonType> get_unchecked(BasicJsonType* ptr) const354 BasicJsonType& get_unchecked(BasicJsonType* ptr) const 355 { 356 for (const auto& reference_token : reference_tokens) 357 { 358 // convert null values to arrays or objects before continuing 359 if (ptr->is_null()) 360 { 361 // check if reference token is a number 362 const bool nums = 363 std::all_of(reference_token.begin(), reference_token.end(), 364 [](const unsigned char x) 365 { 366 return std::isdigit(x); 367 }); 368 369 // change value to array for numbers or "-" or to object otherwise 370 *ptr = (nums || reference_token == "-") 371 ? detail::value_t::array 372 : detail::value_t::object; 373 } 374 375 switch (ptr->type()) 376 { 377 case detail::value_t::object: 378 { 379 // use unchecked object access 380 ptr = &ptr->operator[](reference_token); 381 break; 382 } 383 384 case detail::value_t::array: 385 { 386 if (reference_token == "-") 387 { 388 // explicitly treat "-" as index beyond the end 389 ptr = &ptr->operator[](ptr->m_value.array->size()); 390 } 391 else 392 { 393 // convert array index to number; unchecked access 394 ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token)); 395 } 396 break; 397 } 398 399 case detail::value_t::null: 400 case detail::value_t::string: 401 case detail::value_t::boolean: 402 case detail::value_t::number_integer: 403 case detail::value_t::number_unsigned: 404 case detail::value_t::number_float: 405 case detail::value_t::binary: 406 case detail::value_t::discarded: 407 default: 408 JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); 409 } 410 } 411 412 return *ptr; 413 } 414 415 /*! 416 @throw parse_error.106 if an array index begins with '0' 417 @throw parse_error.109 if an array index was not a number 418 @throw out_of_range.402 if the array index '-' is used 419 @throw out_of_range.404 if the JSON pointer can not be resolved 420 */ 421 template<typename BasicJsonType> get_checked(BasicJsonType* ptr) const422 BasicJsonType& get_checked(BasicJsonType* ptr) const 423 { 424 for (const auto& reference_token : reference_tokens) 425 { 426 switch (ptr->type()) 427 { 428 case detail::value_t::object: 429 { 430 // note: at performs range check 431 ptr = &ptr->at(reference_token); 432 break; 433 } 434 435 case detail::value_t::array: 436 { 437 if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) 438 { 439 // "-" always fails the range check 440 JSON_THROW(detail::out_of_range::create(402, detail::concat( 441 "array index '-' (", std::to_string(ptr->m_value.array->size()), 442 ") is out of range"), ptr)); 443 } 444 445 // note: at performs range check 446 ptr = &ptr->at(array_index<BasicJsonType>(reference_token)); 447 break; 448 } 449 450 case detail::value_t::null: 451 case detail::value_t::string: 452 case detail::value_t::boolean: 453 case detail::value_t::number_integer: 454 case detail::value_t::number_unsigned: 455 case detail::value_t::number_float: 456 case detail::value_t::binary: 457 case detail::value_t::discarded: 458 default: 459 JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); 460 } 461 } 462 463 return *ptr; 464 } 465 466 /*! 467 @brief return a const reference to the pointed to value 468 469 @param[in] ptr a JSON value 470 471 @return const reference to the JSON value pointed to by the JSON 472 pointer 473 474 @throw parse_error.106 if an array index begins with '0' 475 @throw parse_error.109 if an array index was not a number 476 @throw out_of_range.402 if the array index '-' is used 477 @throw out_of_range.404 if the JSON pointer can not be resolved 478 */ 479 template<typename BasicJsonType> get_unchecked(const BasicJsonType* ptr) const480 const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const 481 { 482 for (const auto& reference_token : reference_tokens) 483 { 484 switch (ptr->type()) 485 { 486 case detail::value_t::object: 487 { 488 // use unchecked object access 489 ptr = &ptr->operator[](reference_token); 490 break; 491 } 492 493 case detail::value_t::array: 494 { 495 if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) 496 { 497 // "-" cannot be used for const access 498 JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_value.array->size()), ") is out of range"), ptr)); 499 } 500 501 // use unchecked array access 502 ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token)); 503 break; 504 } 505 506 case detail::value_t::null: 507 case detail::value_t::string: 508 case detail::value_t::boolean: 509 case detail::value_t::number_integer: 510 case detail::value_t::number_unsigned: 511 case detail::value_t::number_float: 512 case detail::value_t::binary: 513 case detail::value_t::discarded: 514 default: 515 JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); 516 } 517 } 518 519 return *ptr; 520 } 521 522 /*! 523 @throw parse_error.106 if an array index begins with '0' 524 @throw parse_error.109 if an array index was not a number 525 @throw out_of_range.402 if the array index '-' is used 526 @throw out_of_range.404 if the JSON pointer can not be resolved 527 */ 528 template<typename BasicJsonType> get_checked(const BasicJsonType* ptr) const529 const BasicJsonType& get_checked(const BasicJsonType* ptr) const 530 { 531 for (const auto& reference_token : reference_tokens) 532 { 533 switch (ptr->type()) 534 { 535 case detail::value_t::object: 536 { 537 // note: at performs range check 538 ptr = &ptr->at(reference_token); 539 break; 540 } 541 542 case detail::value_t::array: 543 { 544 if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) 545 { 546 // "-" always fails the range check 547 JSON_THROW(detail::out_of_range::create(402, detail::concat( 548 "array index '-' (", std::to_string(ptr->m_value.array->size()), 549 ") is out of range"), ptr)); 550 } 551 552 // note: at performs range check 553 ptr = &ptr->at(array_index<BasicJsonType>(reference_token)); 554 break; 555 } 556 557 case detail::value_t::null: 558 case detail::value_t::string: 559 case detail::value_t::boolean: 560 case detail::value_t::number_integer: 561 case detail::value_t::number_unsigned: 562 case detail::value_t::number_float: 563 case detail::value_t::binary: 564 case detail::value_t::discarded: 565 default: 566 JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); 567 } 568 } 569 570 return *ptr; 571 } 572 573 /*! 574 @throw parse_error.106 if an array index begins with '0' 575 @throw parse_error.109 if an array index was not a number 576 */ 577 template<typename BasicJsonType> contains(const BasicJsonType* ptr) const578 bool contains(const BasicJsonType* ptr) const 579 { 580 for (const auto& reference_token : reference_tokens) 581 { 582 switch (ptr->type()) 583 { 584 case detail::value_t::object: 585 { 586 if (!ptr->contains(reference_token)) 587 { 588 // we did not find the key in the object 589 return false; 590 } 591 592 ptr = &ptr->operator[](reference_token); 593 break; 594 } 595 596 case detail::value_t::array: 597 { 598 if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) 599 { 600 // "-" always fails the range check 601 return false; 602 } 603 if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9"))) 604 { 605 // invalid char 606 return false; 607 } 608 if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1)) 609 { 610 if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9'))) 611 { 612 // first char should be between '1' and '9' 613 return false; 614 } 615 for (std::size_t i = 1; i < reference_token.size(); i++) 616 { 617 if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9'))) 618 { 619 // other char should be between '0' and '9' 620 return false; 621 } 622 } 623 } 624 625 const auto idx = array_index<BasicJsonType>(reference_token); 626 if (idx >= ptr->size()) 627 { 628 // index out of range 629 return false; 630 } 631 632 ptr = &ptr->operator[](idx); 633 break; 634 } 635 636 case detail::value_t::null: 637 case detail::value_t::string: 638 case detail::value_t::boolean: 639 case detail::value_t::number_integer: 640 case detail::value_t::number_unsigned: 641 case detail::value_t::number_float: 642 case detail::value_t::binary: 643 case detail::value_t::discarded: 644 default: 645 { 646 // we do not expect primitive values if there is still a 647 // reference token to process 648 return false; 649 } 650 } 651 } 652 653 // no reference token left means we found a primitive value 654 return true; 655 } 656 657 /*! 658 @brief split the string input to reference tokens 659 660 @note This function is only called by the json_pointer constructor. 661 All exceptions below are documented there. 662 663 @throw parse_error.107 if the pointer is not empty or begins with '/' 664 @throw parse_error.108 if character '~' is not followed by '0' or '1' 665 */ split(const string_t& reference_string)666 static std::vector<string_t> split(const string_t& reference_string) 667 { 668 std::vector<string_t> result; 669 670 // special case: empty reference string -> no reference tokens 671 if (reference_string.empty()) 672 { 673 return result; 674 } 675 676 // check if nonempty reference string begins with slash 677 if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) 678 { 679 JSON_THROW(detail::parse_error::create(107, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr)); 680 } 681 682 // extract the reference tokens: 683 // - slash: position of the last read slash (or end of string) 684 // - start: position after the previous slash 685 for ( 686 // search for the first slash after the first character 687 std::size_t slash = reference_string.find_first_of('/', 1), 688 // set the beginning of the first reference token 689 start = 1; 690 // we can stop if start == 0 (if slash == string_t::npos) 691 start != 0; 692 // set the beginning of the next reference token 693 // (will eventually be 0 if slash == string_t::npos) 694 start = (slash == string_t::npos) ? 0 : slash + 1, 695 // find next slash 696 slash = reference_string.find_first_of('/', start)) 697 { 698 // use the text between the beginning of the reference token 699 // (start) and the last slash (slash). 700 auto reference_token = reference_string.substr(start, slash - start); 701 702 // check reference tokens are properly escaped 703 for (std::size_t pos = reference_token.find_first_of('~'); 704 pos != string_t::npos; 705 pos = reference_token.find_first_of('~', pos + 1)) 706 { 707 JSON_ASSERT(reference_token[pos] == '~'); 708 709 // ~ must be followed by 0 or 1 710 if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 || 711 (reference_token[pos + 1] != '0' && 712 reference_token[pos + 1] != '1'))) 713 { 714 JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", nullptr)); 715 } 716 } 717 718 // finally, store the reference token 719 detail::unescape(reference_token); 720 result.push_back(reference_token); 721 } 722 723 return result; 724 } 725 726 private: 727 /*! 728 @param[in] reference_string the reference string to the current value 729 @param[in] value the value to consider 730 @param[in,out] result the result object to insert values to 731 732 @note Empty objects or arrays are flattened to `null`. 733 */ 734 template<typename BasicJsonType> flatten(const string_t& reference_string, const BasicJsonType& value, BasicJsonType& result)735 static void flatten(const string_t& reference_string, 736 const BasicJsonType& value, 737 BasicJsonType& result) 738 { 739 switch (value.type()) 740 { 741 case detail::value_t::array: 742 { 743 if (value.m_value.array->empty()) 744 { 745 // flatten empty array as null 746 result[reference_string] = nullptr; 747 } 748 else 749 { 750 // iterate array and use index as reference string 751 for (std::size_t i = 0; i < value.m_value.array->size(); ++i) 752 { 753 flatten(detail::concat(reference_string, '/', std::to_string(i)), 754 value.m_value.array->operator[](i), result); 755 } 756 } 757 break; 758 } 759 760 case detail::value_t::object: 761 { 762 if (value.m_value.object->empty()) 763 { 764 // flatten empty object as null 765 result[reference_string] = nullptr; 766 } 767 else 768 { 769 // iterate object and use keys as reference string 770 for (const auto& element : *value.m_value.object) 771 { 772 flatten(detail::concat(reference_string, '/', detail::escape(element.first)), element.second, result); 773 } 774 } 775 break; 776 } 777 778 case detail::value_t::null: 779 case detail::value_t::string: 780 case detail::value_t::boolean: 781 case detail::value_t::number_integer: 782 case detail::value_t::number_unsigned: 783 case detail::value_t::number_float: 784 case detail::value_t::binary: 785 case detail::value_t::discarded: 786 default: 787 { 788 // add primitive value with its reference string 789 result[reference_string] = value; 790 break; 791 } 792 } 793 } 794 795 /*! 796 @param[in] value flattened JSON 797 798 @return unflattened JSON 799 800 @throw parse_error.109 if array index is not a number 801 @throw type_error.314 if value is not an object 802 @throw type_error.315 if object values are not primitive 803 @throw type_error.313 if value cannot be unflattened 804 */ 805 template<typename BasicJsonType> 806 static BasicJsonType unflatten(const BasicJsonType& value)807 unflatten(const BasicJsonType& value) 808 { 809 if (JSON_HEDLEY_UNLIKELY(!value.is_object())) 810 { 811 JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", &value)); 812 } 813 814 BasicJsonType result; 815 816 // iterate the JSON object values 817 for (const auto& element : *value.m_value.object) 818 { 819 if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive())) 820 { 821 JSON_THROW(detail::type_error::create(315, "values in object must be primitive", &element.second)); 822 } 823 824 // assign value to reference pointed to by JSON pointer; Note that if 825 // the JSON pointer is "" (i.e., points to the whole value), function 826 // get_and_create returns a reference to result itself. An assignment 827 // will then create a primitive value. 828 json_pointer(element.first).get_and_create(result) = element.second; 829 } 830 831 return result; 832 } 833 834 // can't use conversion operator because of ambiguity convert() const835 json_pointer<string_t> convert() const& 836 { 837 json_pointer<string_t> result; 838 result.reference_tokens = reference_tokens; 839 return result; 840 } 841 convert()842 json_pointer<string_t> convert()&& 843 { 844 json_pointer<string_t> result; 845 result.reference_tokens = std::move(reference_tokens); 846 return result; 847 } 848 849 public: 850 #if JSON_HAS_THREE_WAY_COMPARISON 851 /// @brief compares two JSON pointers for equality 852 /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/ 853 template<typename RefStringTypeRhs> 854 bool operator==(const json_pointer<RefStringTypeRhs>& rhs) const noexcept 855 { 856 return reference_tokens == rhs.reference_tokens; 857 } 858 859 /// @brief compares JSON pointer and string for equality 860 /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/ 861 JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer)) operator ==(const string_t& rhs) const862 bool operator==(const string_t& rhs) const 863 { 864 return *this == json_pointer(rhs); 865 } 866 867 /// @brief 3-way compares two JSON pointers 868 template<typename RefStringTypeRhs> 869 std::strong_ordering operator<=>(const json_pointer<RefStringTypeRhs>& rhs) const noexcept // *NOPAD* 870 { 871 return reference_tokens <=> rhs.reference_tokens; // *NOPAD* 872 } 873 #else 874 /// @brief compares two JSON pointers for equality 875 /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/ 876 template<typename RefStringTypeLhs, typename RefStringTypeRhs> 877 // NOLINTNEXTLINE(readability-redundant-declaration) 878 friend bool operator==(const json_pointer<RefStringTypeLhs>& lhs, 879 const json_pointer<RefStringTypeRhs>& rhs) noexcept; 880 881 /// @brief compares JSON pointer and string for equality 882 /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/ 883 template<typename RefStringTypeLhs, typename StringType> 884 // NOLINTNEXTLINE(readability-redundant-declaration) 885 friend bool operator==(const json_pointer<RefStringTypeLhs>& lhs, 886 const StringType& rhs); 887 888 /// @brief compares string and JSON pointer for equality 889 /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/ 890 template<typename RefStringTypeRhs, typename StringType> 891 // NOLINTNEXTLINE(readability-redundant-declaration) 892 friend bool operator==(const StringType& lhs, 893 const json_pointer<RefStringTypeRhs>& rhs); 894 895 /// @brief compares two JSON pointers for inequality 896 /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/ 897 template<typename RefStringTypeLhs, typename RefStringTypeRhs> 898 // NOLINTNEXTLINE(readability-redundant-declaration) 899 friend bool operator!=(const json_pointer<RefStringTypeLhs>& lhs, 900 const json_pointer<RefStringTypeRhs>& rhs) noexcept; 901 902 /// @brief compares JSON pointer and string for inequality 903 /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/ 904 template<typename RefStringTypeLhs, typename StringType> 905 // NOLINTNEXTLINE(readability-redundant-declaration) 906 friend bool operator!=(const json_pointer<RefStringTypeLhs>& lhs, 907 const StringType& rhs); 908 909 /// @brief compares string and JSON pointer for inequality 910 /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/ 911 template<typename RefStringTypeRhs, typename StringType> 912 // NOLINTNEXTLINE(readability-redundant-declaration) 913 friend bool operator!=(const StringType& lhs, 914 const json_pointer<RefStringTypeRhs>& rhs); 915 916 /// @brief compares two JSON pointer for less-than 917 template<typename RefStringTypeLhs, typename RefStringTypeRhs> 918 // NOLINTNEXTLINE(readability-redundant-declaration) 919 friend bool operator<(const json_pointer<RefStringTypeLhs>& lhs, 920 const json_pointer<RefStringTypeRhs>& rhs) noexcept; 921 #endif 922 923 private: 924 /// the reference tokens 925 std::vector<string_t> reference_tokens; 926 }; 927 928 #if !JSON_HAS_THREE_WAY_COMPARISON 929 // functions cannot be defined inside class due to ODR violations 930 template<typename RefStringTypeLhs, typename RefStringTypeRhs> 931 inline bool operator==(const json_pointer<RefStringTypeLhs>& lhs, 932 const json_pointer<RefStringTypeRhs>& rhs) noexcept 933 { 934 return lhs.reference_tokens == rhs.reference_tokens; 935 } 936 937 template<typename RefStringTypeLhs, 938 typename StringType = typename json_pointer<RefStringTypeLhs>::string_t> 939 JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer)) 940 inline bool operator==(const json_pointer<RefStringTypeLhs>& lhs, 941 const StringType& rhs) 942 { 943 return lhs == json_pointer<RefStringTypeLhs>(rhs); 944 } 945 946 template<typename RefStringTypeRhs, 947 typename StringType = typename json_pointer<RefStringTypeRhs>::string_t> 948 JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer)) 949 inline bool operator==(const StringType& lhs, 950 const json_pointer<RefStringTypeRhs>& rhs) 951 { 952 return json_pointer<RefStringTypeRhs>(lhs) == rhs; 953 } 954 955 template<typename RefStringTypeLhs, typename RefStringTypeRhs> 956 inline bool operator!=(const json_pointer<RefStringTypeLhs>& lhs, 957 const json_pointer<RefStringTypeRhs>& rhs) noexcept 958 { 959 return !(lhs == rhs); 960 } 961 962 template<typename RefStringTypeLhs, 963 typename StringType = typename json_pointer<RefStringTypeLhs>::string_t> 964 JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer)) 965 inline bool operator!=(const json_pointer<RefStringTypeLhs>& lhs, 966 const StringType& rhs) 967 { 968 return !(lhs == rhs); 969 } 970 971 template<typename RefStringTypeRhs, 972 typename StringType = typename json_pointer<RefStringTypeRhs>::string_t> 973 JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer)) 974 inline bool operator!=(const StringType& lhs, 975 const json_pointer<RefStringTypeRhs>& rhs) 976 { 977 return !(lhs == rhs); 978 } 979 980 template<typename RefStringTypeLhs, typename RefStringTypeRhs> 981 inline bool operator<(const json_pointer<RefStringTypeLhs>& lhs, 982 const json_pointer<RefStringTypeRhs>& rhs) noexcept 983 { 984 return lhs.reference_tokens < rhs.reference_tokens; 985 } 986 #endif 987 988 NLOHMANN_JSON_NAMESPACE_END 989