| // __ _____ _____ _____ |
| // __| | __| | | | JSON for Modern C++ |
| // | | |__ | | | | | | version 3.11.3 |
| // |_____|_____|_____|_|___| https://github.com/nlohmann/json |
| // |
| // SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me> |
| // SPDX-License-Identifier: MIT |
| |
| #pragma once |
| |
| #include <algorithm> // all_of |
| #include <cctype> // isdigit |
| #include <cerrno> // errno, ERANGE |
| #include <cstdlib> // strtoull |
| #ifndef JSON_NO_IO |
| #include <iosfwd> // ostream |
| #endif // JSON_NO_IO |
| #include <limits> // max |
| #include <numeric> // accumulate |
| #include <string> // string |
| #include <utility> // move |
| #include <vector> // vector |
| |
| #include <nlohmann/detail/exceptions.hpp> |
| #include <nlohmann/detail/macro_scope.hpp> |
| #include <nlohmann/detail/string_concat.hpp> |
| #include <nlohmann/detail/string_escape.hpp> |
| #include <nlohmann/detail/value_t.hpp> |
| |
| NLOHMANN_JSON_NAMESPACE_BEGIN |
| |
| /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document |
| /// @sa https://json.nlohmann.me/api/json_pointer/ |
| template<typename RefStringType> |
| class json_pointer |
| { |
| // allow basic_json to access private members |
| NLOHMANN_BASIC_JSON_TPL_DECLARATION |
| friend class basic_json; |
| |
| template<typename> |
| friend class json_pointer; |
| |
| template<typename T> |
| struct string_t_helper |
| { |
| using type = T; |
| }; |
| |
| NLOHMANN_BASIC_JSON_TPL_DECLARATION |
| struct string_t_helper<NLOHMANN_BASIC_JSON_TPL> |
| { |
| using type = StringType; |
| }; |
| |
| public: |
| // for backwards compatibility accept BasicJsonType |
| using string_t = typename string_t_helper<RefStringType>::type; |
| |
| /// @brief create JSON pointer |
| /// @sa https://json.nlohmann.me/api/json_pointer/json_pointer/ |
| explicit json_pointer(const string_t& s = "") |
| : reference_tokens(split(s)) |
| {} |
| |
| /// @brief return a string representation of the JSON pointer |
| /// @sa https://json.nlohmann.me/api/json_pointer/to_string/ |
| string_t to_string() const |
| { |
| return std::accumulate(reference_tokens.begin(), reference_tokens.end(), string_t{}, [](const string_t& a, const string_t& b) { |
| return detail::concat(a, '/', detail::escape(b)); |
| }); |
| } |
| |
| /// @brief return a string representation of the JSON pointer |
| /// @sa https://json.nlohmann.me/api/json_pointer/operator_string/ |
| JSON_HEDLEY_DEPRECATED_FOR(3.11.0, to_string()) |
| operator string_t() const |
| { |
| return to_string(); |
| } |
| |
| #ifndef JSON_NO_IO |
| /// @brief write string representation of the JSON pointer to stream |
| /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/ |
| friend std::ostream& operator<<(std::ostream& o, const json_pointer& ptr) |
| { |
| o << ptr.to_string(); |
| return o; |
| } |
| #endif |
| |
| /// @brief append another JSON pointer at the end of this JSON pointer |
| /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/ |
| json_pointer& operator/=(const json_pointer& ptr) |
| { |
| reference_tokens.insert(reference_tokens.end(), ptr.reference_tokens.begin(), ptr.reference_tokens.end()); |
| return *this; |
| } |
| |
| /// @brief append an unescaped reference token at the end of this JSON pointer |
| /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/ |
| json_pointer& operator/=(string_t token) |
| { |
| push_back(std::move(token)); |
| return *this; |
| } |
| |
| /// @brief append an array index at the end of this JSON pointer |
| /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/ |
| json_pointer& operator/=(std::size_t array_idx) |
| { |
| return *this /= std::to_string(array_idx); |
| } |
| |
| /// @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer |
| /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ |
| friend json_pointer operator/(const json_pointer& lhs, const json_pointer& rhs) |
| { |
| return json_pointer(lhs) /= rhs; |
| } |
| |
| /// @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer |
| /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ |
| friend json_pointer operator/(const json_pointer& lhs, string_t token) // NOLINT(performance-unnecessary-value-param) |
| { |
| return json_pointer(lhs) /= std::move(token); |
| } |
| |
| /// @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer |
| /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ |
| friend json_pointer operator/(const json_pointer& lhs, std::size_t array_idx) |
| { |
| return json_pointer(lhs) /= array_idx; |
| } |
| |
| /// @brief returns the parent of this JSON pointer |
| /// @sa https://json.nlohmann.me/api/json_pointer/parent_pointer/ |
| json_pointer parent_pointer() const |
| { |
| if (empty()) |
| { |
| return *this; |
| } |
| |
| json_pointer res = *this; |
| res.pop_back(); |
| return res; |
| } |
| |
| /// @brief remove last reference token |
| /// @sa https://json.nlohmann.me/api/json_pointer/pop_back/ |
| void pop_back() |
| { |
| if (JSON_HEDLEY_UNLIKELY(empty())) |
| { |
| JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr)); |
| } |
| |
| reference_tokens.pop_back(); |
| } |
| |
| /// @brief return last reference token |
| /// @sa https://json.nlohmann.me/api/json_pointer/back/ |
| const string_t& back() const |
| { |
| if (JSON_HEDLEY_UNLIKELY(empty())) |
| { |
| JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr)); |
| } |
| |
| return reference_tokens.back(); |
| } |
| |
| /// @brief append an unescaped token at the end of the reference pointer |
| /// @sa https://json.nlohmann.me/api/json_pointer/push_back/ |
| void push_back(const string_t& token) |
| { |
| reference_tokens.push_back(token); |
| } |
| |
| /// @brief append an unescaped token at the end of the reference pointer |
| /// @sa https://json.nlohmann.me/api/json_pointer/push_back/ |
| void push_back(string_t&& token) |
| { |
| reference_tokens.push_back(std::move(token)); |
| } |
| |
| /// @brief return whether pointer points to the root document |
| /// @sa https://json.nlohmann.me/api/json_pointer/empty/ |
| bool empty() const noexcept |
| { |
| return reference_tokens.empty(); |
| } |
| |
| private: |
| /*! |
| @param[in] s reference token to be converted into an array index |
| |
| @return integer representation of @a s |
| |
| @throw parse_error.106 if an array index begins with '0' |
| @throw parse_error.109 if an array index begins not with a digit |
| @throw out_of_range.404 if string @a s could not be converted to an integer |
| @throw out_of_range.410 if an array index exceeds size_type |
| */ |
| template<typename BasicJsonType> |
| static typename BasicJsonType::size_type array_index(const string_t& s) |
| { |
| using size_type = typename BasicJsonType::size_type; |
| |
| // error condition (cf. RFC 6901, Sect. 4) |
| if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0')) |
| { |
| JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr)); |
| } |
| |
| // error condition (cf. RFC 6901, Sect. 4) |
| if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9'))) |
| { |
| JSON_THROW(detail::parse_error::create(109, 0, detail::concat("array index '", s, "' is not a number"), nullptr)); |
| } |
| |
| const char* p = s.c_str(); |
| char* p_end = nullptr; |
| errno = 0; // strtoull doesn't reset errno |
| const unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int) |
| if (p == p_end // invalid input or empty string |
| || errno == ERANGE // out of range |
| || JSON_HEDLEY_UNLIKELY(static_cast<std::size_t>(p_end - p) != s.size())) // incomplete read |
| { |
| JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", s, "'"), nullptr)); |
| } |
| |
| // only triggered on special platforms (like 32bit), see also |
| // https://github.com/nlohmann/json/pull/2203 |
| if (res >= static_cast<unsigned long long>((std::numeric_limits<size_type>::max)())) // NOLINT(runtime/int) |
| { |
| JSON_THROW(detail::out_of_range::create(410, detail::concat("array index ", s, " exceeds size_type"), nullptr)); // LCOV_EXCL_LINE |
| } |
| |
| return static_cast<size_type>(res); |
| } |
| |
| JSON_PRIVATE_UNLESS_TESTED : |
| json_pointer top() const |
| { |
| if (JSON_HEDLEY_UNLIKELY(empty())) |
| { |
| JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr)); |
| } |
| |
| json_pointer result = *this; |
| result.reference_tokens = { reference_tokens[0] }; |
| return result; |
| } |
| |
| private: |
| /*! |
| @brief create and return a reference to the pointed to value |
| |
| @complexity Linear in the number of reference tokens. |
| |
| @throw parse_error.109 if array index is not a number |
| @throw type_error.313 if value cannot be unflattened |
| */ |
| template<typename BasicJsonType> |
| BasicJsonType& get_and_create(BasicJsonType& j) const |
| { |
| auto* result = &j; |
| |
| // in case no reference tokens exist, return a reference to the JSON value |
| // j which will be overwritten by a primitive value |
| for (const auto& reference_token : reference_tokens) |
| { |
| switch (result->type()) |
| { |
| case detail::value_t::null: |
| { |
| if (reference_token == "0") |
| { |
| // start a new array if reference token is 0 |
| result = &result->operator[](0); |
| } |
| else |
| { |
| // start a new object otherwise |
| result = &result->operator[](reference_token); |
| } |
| break; |
| } |
| |
| case detail::value_t::object: |
| { |
| // create an entry in the object |
| result = &result->operator[](reference_token); |
| break; |
| } |
| |
| case detail::value_t::array: |
| { |
| // create an entry in the array |
| result = &result->operator[](array_index<BasicJsonType>(reference_token)); |
| break; |
| } |
| |
| /* |
| The following code is only reached if there exists a reference |
| token _and_ the current value is primitive. In this case, we have |
| an error situation, because primitive values may only occur as |
| single value; that is, with an empty list of reference tokens. |
| */ |
| case detail::value_t::string: |
| case detail::value_t::boolean: |
| case detail::value_t::number_integer: |
| case detail::value_t::number_unsigned: |
| case detail::value_t::number_float: |
| case detail::value_t::binary: |
| case detail::value_t::discarded: |
| default: |
| JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", &j)); |
| } |
| } |
| |
| return *result; |
| } |
| |
| /*! |
| @brief return a reference to the pointed to value |
| |
| @note This version does not throw if a value is not present, but tries to |
| create nested values instead. For instance, calling this function |
| with pointer `"/this/that"` on a null value is equivalent to calling |
| `operator[]("this").operator[]("that")` on that value, effectively |
| changing the null value to an object. |
| |
| @param[in] ptr a JSON value |
| |
| @return reference to the JSON value pointed to by the JSON pointer |
| |
| @complexity Linear in the length of the JSON pointer. |
| |
| @throw parse_error.106 if an array index begins with '0' |
| @throw parse_error.109 if an array index was not a number |
| @throw out_of_range.404 if the JSON pointer can not be resolved |
| */ |
| template<typename BasicJsonType> |
| BasicJsonType& get_unchecked(BasicJsonType* ptr) const |
| { |
| for (const auto& reference_token : reference_tokens) |
| { |
| // convert null values to arrays or objects before continuing |
| if (ptr->is_null()) |
| { |
| // check if reference token is a number |
| const bool nums = std::all_of(reference_token.begin(), reference_token.end(), [](const unsigned char x) { |
| return std::isdigit(x); |
| }); |
| |
| // change value to array for numbers or "-" or to object otherwise |
| *ptr = (nums || reference_token == "-") ? detail::value_t::array : detail::value_t::object; |
| } |
| |
| switch (ptr->type()) |
| { |
| case detail::value_t::object: |
| { |
| // use unchecked object access |
| ptr = &ptr->operator[](reference_token); |
| break; |
| } |
| |
| case detail::value_t::array: |
| { |
| if (reference_token == "-") |
| { |
| // explicitly treat "-" as index beyond the end |
| ptr = &ptr->operator[](ptr->m_data.m_value.array->size()); |
| } |
| else |
| { |
| // convert array index to number; unchecked access |
| ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token)); |
| } |
| break; |
| } |
| |
| case detail::value_t::null: |
| case detail::value_t::string: |
| case detail::value_t::boolean: |
| case detail::value_t::number_integer: |
| case detail::value_t::number_unsigned: |
| case detail::value_t::number_float: |
| case detail::value_t::binary: |
| case detail::value_t::discarded: |
| default: |
| JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); |
| } |
| } |
| |
| return *ptr; |
| } |
| |
| /*! |
| @throw parse_error.106 if an array index begins with '0' |
| @throw parse_error.109 if an array index was not a number |
| @throw out_of_range.402 if the array index '-' is used |
| @throw out_of_range.404 if the JSON pointer can not be resolved |
| */ |
| template<typename BasicJsonType> |
| BasicJsonType& get_checked(BasicJsonType* ptr) const |
| { |
| for (const auto& reference_token : reference_tokens) |
| { |
| switch (ptr->type()) |
| { |
| case detail::value_t::object: |
| { |
| // note: at performs range check |
| ptr = &ptr->at(reference_token); |
| break; |
| } |
| |
| case detail::value_t::array: |
| { |
| if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) |
| { |
| // "-" always fails the range check |
| JSON_THROW(detail::out_of_range::create( |
| 402, |
| detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), |
| ptr)); |
| } |
| |
| // note: at performs range check |
| ptr = &ptr->at(array_index<BasicJsonType>(reference_token)); |
| break; |
| } |
| |
| case detail::value_t::null: |
| case detail::value_t::string: |
| case detail::value_t::boolean: |
| case detail::value_t::number_integer: |
| case detail::value_t::number_unsigned: |
| case detail::value_t::number_float: |
| case detail::value_t::binary: |
| case detail::value_t::discarded: |
| default: |
| JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); |
| } |
| } |
| |
| return *ptr; |
| } |
| |
| /*! |
| @brief return a const reference to the pointed to value |
| |
| @param[in] ptr a JSON value |
| |
| @return const reference to the JSON value pointed to by the JSON |
| pointer |
| |
| @throw parse_error.106 if an array index begins with '0' |
| @throw parse_error.109 if an array index was not a number |
| @throw out_of_range.402 if the array index '-' is used |
| @throw out_of_range.404 if the JSON pointer can not be resolved |
| */ |
| template<typename BasicJsonType> |
| const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const |
| { |
| for (const auto& reference_token : reference_tokens) |
| { |
| switch (ptr->type()) |
| { |
| case detail::value_t::object: |
| { |
| // use unchecked object access |
| ptr = &ptr->operator[](reference_token); |
| break; |
| } |
| |
| case detail::value_t::array: |
| { |
| if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) |
| { |
| // "-" cannot be used for const access |
| JSON_THROW(detail::out_of_range::create( |
| 402, |
| detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), |
| ptr)); |
| } |
| |
| // use unchecked array access |
| ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token)); |
| break; |
| } |
| |
| case detail::value_t::null: |
| case detail::value_t::string: |
| case detail::value_t::boolean: |
| case detail::value_t::number_integer: |
| case detail::value_t::number_unsigned: |
| case detail::value_t::number_float: |
| case detail::value_t::binary: |
| case detail::value_t::discarded: |
| default: |
| JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); |
| } |
| } |
| |
| return *ptr; |
| } |
| |
| /*! |
| @throw parse_error.106 if an array index begins with '0' |
| @throw parse_error.109 if an array index was not a number |
| @throw out_of_range.402 if the array index '-' is used |
| @throw out_of_range.404 if the JSON pointer can not be resolved |
| */ |
| template<typename BasicJsonType> |
| const BasicJsonType& get_checked(const BasicJsonType* ptr) const |
| { |
| for (const auto& reference_token : reference_tokens) |
| { |
| switch (ptr->type()) |
| { |
| case detail::value_t::object: |
| { |
| // note: at performs range check |
| ptr = &ptr->at(reference_token); |
| break; |
| } |
| |
| case detail::value_t::array: |
| { |
| if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) |
| { |
| // "-" always fails the range check |
| JSON_THROW(detail::out_of_range::create( |
| 402, |
| detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), |
| ptr)); |
| } |
| |
| // note: at performs range check |
| ptr = &ptr->at(array_index<BasicJsonType>(reference_token)); |
| break; |
| } |
| |
| case detail::value_t::null: |
| case detail::value_t::string: |
| case detail::value_t::boolean: |
| case detail::value_t::number_integer: |
| case detail::value_t::number_unsigned: |
| case detail::value_t::number_float: |
| case detail::value_t::binary: |
| case detail::value_t::discarded: |
| default: |
| JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); |
| } |
| } |
| |
| return *ptr; |
| } |
| |
| /*! |
| @throw parse_error.106 if an array index begins with '0' |
| @throw parse_error.109 if an array index was not a number |
| */ |
| template<typename BasicJsonType> |
| bool contains(const BasicJsonType* ptr) const |
| { |
| for (const auto& reference_token : reference_tokens) |
| { |
| switch (ptr->type()) |
| { |
| case detail::value_t::object: |
| { |
| if (!ptr->contains(reference_token)) |
| { |
| // we did not find the key in the object |
| return false; |
| } |
| |
| ptr = &ptr->operator[](reference_token); |
| break; |
| } |
| |
| case detail::value_t::array: |
| { |
| if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) |
| { |
| // "-" always fails the range check |
| return false; |
| } |
| if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9"))) |
| { |
| // invalid char |
| return false; |
| } |
| if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1)) |
| { |
| if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9'))) |
| { |
| // first char should be between '1' and '9' |
| return false; |
| } |
| for (std::size_t i = 1; i < reference_token.size(); i++) |
| { |
| if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9'))) |
| { |
| // other char should be between '0' and '9' |
| return false; |
| } |
| } |
| } |
| |
| const auto idx = array_index<BasicJsonType>(reference_token); |
| if (idx >= ptr->size()) |
| { |
| // index out of range |
| return false; |
| } |
| |
| ptr = &ptr->operator[](idx); |
| break; |
| } |
| |
| case detail::value_t::null: |
| case detail::value_t::string: |
| case detail::value_t::boolean: |
| case detail::value_t::number_integer: |
| case detail::value_t::number_unsigned: |
| case detail::value_t::number_float: |
| case detail::value_t::binary: |
| case detail::value_t::discarded: |
| default: |
| { |
| // we do not expect primitive values if there is still a |
| // reference token to process |
| return false; |
| } |
| } |
| } |
| |
| // no reference token left means we found a primitive value |
| return true; |
| } |
| |
| /*! |
| @brief split the string input to reference tokens |
| |
| @note This function is only called by the json_pointer constructor. |
| All exceptions below are documented there. |
| |
| @throw parse_error.107 if the pointer is not empty or begins with '/' |
| @throw parse_error.108 if character '~' is not followed by '0' or '1' |
| */ |
| static std::vector<string_t> split(const string_t& reference_string) |
| { |
| std::vector<string_t> result; |
| |
| // special case: empty reference string -> no reference tokens |
| if (reference_string.empty()) |
| { |
| return result; |
| } |
| |
| // check if nonempty reference string begins with slash |
| if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) |
| { |
| JSON_THROW( |
| detail::parse_error::create(107, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr)); |
| } |
| |
| // extract the reference tokens: |
| // - slash: position of the last read slash (or end of string) |
| // - start: position after the previous slash |
| for ( |
| // search for the first slash after the first character |
| std::size_t slash = reference_string.find_first_of('/', 1), |
| // set the beginning of the first reference token |
| start = 1; |
| // we can stop if start == 0 (if slash == string_t::npos) |
| start != 0; |
| // set the beginning of the next reference token |
| // (will eventually be 0 if slash == string_t::npos) |
| start = (slash == string_t::npos) ? 0 : slash + 1, |
| // find next slash |
| slash = reference_string.find_first_of('/', start)) |
| { |
| // use the text between the beginning of the reference token |
| // (start) and the last slash (slash). |
| auto reference_token = reference_string.substr(start, slash - start); |
| |
| // check reference tokens are properly escaped |
| for (std::size_t pos = reference_token.find_first_of('~'); pos != string_t::npos; pos = reference_token.find_first_of('~', pos + 1)) |
| { |
| JSON_ASSERT(reference_token[pos] == '~'); |
| |
| // ~ must be followed by 0 or 1 |
| if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 || (reference_token[pos + 1] != '0' && reference_token[pos + 1] != '1'))) |
| { |
| JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", nullptr)); |
| } |
| } |
| |
| // finally, store the reference token |
| detail::unescape(reference_token); |
| result.push_back(reference_token); |
| } |
| |
| return result; |
| } |
| |
| private: |
| /*! |
| @param[in] reference_string the reference string to the current value |
| @param[in] value the value to consider |
| @param[in,out] result the result object to insert values to |
| |
| @note Empty objects or arrays are flattened to `null`. |
| */ |
| template<typename BasicJsonType> |
| static void flatten(const string_t& reference_string, const BasicJsonType& value, BasicJsonType& result) |
| { |
| switch (value.type()) |
| { |
| case detail::value_t::array: |
| { |
| if (value.m_data.m_value.array->empty()) |
| { |
| // flatten empty array as null |
| result[reference_string] = nullptr; |
| } |
| else |
| { |
| // iterate array and use index as reference string |
| for (std::size_t i = 0; i < value.m_data.m_value.array->size(); ++i) |
| { |
| flatten(detail::concat(reference_string, '/', std::to_string(i)), value.m_data.m_value.array->operator[](i), result); |
| } |
| } |
| break; |
| } |
| |
| case detail::value_t::object: |
| { |
| if (value.m_data.m_value.object->empty()) |
| { |
| // flatten empty object as null |
| result[reference_string] = nullptr; |
| } |
| else |
| { |
| // iterate object and use keys as reference string |
| for (const auto& element : *value.m_data.m_value.object) |
| { |
| flatten(detail::concat(reference_string, '/', detail::escape(element.first)), element.second, result); |
| } |
| } |
| break; |
| } |
| |
| case detail::value_t::null: |
| case detail::value_t::string: |
| case detail::value_t::boolean: |
| case detail::value_t::number_integer: |
| case detail::value_t::number_unsigned: |
| case detail::value_t::number_float: |
| case detail::value_t::binary: |
| case detail::value_t::discarded: |
| default: |
| { |
| // add primitive value with its reference string |
| result[reference_string] = value; |
| break; |
| } |
| } |
| } |
| |
| /*! |
| @param[in] value flattened JSON |
| |
| @return unflattened JSON |
| |
| @throw parse_error.109 if array index is not a number |
| @throw type_error.314 if value is not an object |
| @throw type_error.315 if object values are not primitive |
| @throw type_error.313 if value cannot be unflattened |
| */ |
| template<typename BasicJsonType> |
| static BasicJsonType unflatten(const BasicJsonType& value) |
| { |
| if (JSON_HEDLEY_UNLIKELY(!value.is_object())) |
| { |
| JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", &value)); |
| } |
| |
| BasicJsonType result; |
| |
| // iterate the JSON object values |
| for (const auto& element : *value.m_data.m_value.object) |
| { |
| if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive())) |
| { |
| JSON_THROW(detail::type_error::create(315, "values in object must be primitive", &element.second)); |
| } |
| |
| // assign value to reference pointed to by JSON pointer; Note that if |
| // the JSON pointer is "" (i.e., points to the whole value), function |
| // get_and_create returns a reference to result itself. An assignment |
| // will then create a primitive value. |
| json_pointer(element.first).get_and_create(result) = element.second; |
| } |
| |
| return result; |
| } |
| |
| // can't use conversion operator because of ambiguity |
| json_pointer<string_t> convert() const& |
| { |
| json_pointer<string_t> result; |
| result.reference_tokens = reference_tokens; |
| return result; |
| } |
| |
| json_pointer<string_t> convert() && |
| { |
| json_pointer<string_t> result; |
| result.reference_tokens = std::move(reference_tokens); |
| return result; |
| } |
| |
| public: |
| #if JSON_HAS_THREE_WAY_COMPARISON |
| /// @brief compares two JSON pointers for equality |
| /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/ |
| template<typename RefStringTypeRhs> |
| bool operator==(const json_pointer<RefStringTypeRhs>& rhs) const noexcept |
| { |
| return reference_tokens == rhs.reference_tokens; |
| } |
| |
| /// @brief compares JSON pointer and string for equality |
| /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/ |
| JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer)) |
| bool operator==(const string_t& rhs) const |
| { |
| return *this == json_pointer(rhs); |
| } |
| |
| /// @brief 3-way compares two JSON pointers |
| template<typename RefStringTypeRhs> |
| std::strong_ordering operator<=>(const json_pointer<RefStringTypeRhs>& rhs) const noexcept // *NOPAD* |
| { |
| return reference_tokens <=> rhs.reference_tokens; // *NOPAD* |
| } |
| #else |
| /// @brief compares two JSON pointers for equality |
| /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/ |
| template<typename RefStringTypeLhs, typename RefStringTypeRhs> |
| // NOLINTNEXTLINE(readability-redundant-declaration) |
| friend bool operator==(const json_pointer<RefStringTypeLhs>& lhs, const json_pointer<RefStringTypeRhs>& rhs) noexcept; |
| |
| /// @brief compares JSON pointer and string for equality |
| /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/ |
| template<typename RefStringTypeLhs, typename StringType> |
| // NOLINTNEXTLINE(readability-redundant-declaration) |
| friend bool operator==(const json_pointer<RefStringTypeLhs>& lhs, const StringType& rhs); |
| |
| /// @brief compares string and JSON pointer for equality |
| /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/ |
| template<typename RefStringTypeRhs, typename StringType> |
| // NOLINTNEXTLINE(readability-redundant-declaration) |
| friend bool operator==(const StringType& lhs, const json_pointer<RefStringTypeRhs>& rhs); |
| |
| /// @brief compares two JSON pointers for inequality |
| /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/ |
| template<typename RefStringTypeLhs, typename RefStringTypeRhs> |
| // NOLINTNEXTLINE(readability-redundant-declaration) |
| friend bool operator!=(const json_pointer<RefStringTypeLhs>& lhs, const json_pointer<RefStringTypeRhs>& rhs) noexcept; |
| |
| /// @brief compares JSON pointer and string for inequality |
| /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/ |
| template<typename RefStringTypeLhs, typename StringType> |
| // NOLINTNEXTLINE(readability-redundant-declaration) |
| friend bool operator!=(const json_pointer<RefStringTypeLhs>& lhs, const StringType& rhs); |
| |
| /// @brief compares string and JSON pointer for inequality |
| /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/ |
| template<typename RefStringTypeRhs, typename StringType> |
| // NOLINTNEXTLINE(readability-redundant-declaration) |
| friend bool operator!=(const StringType& lhs, const json_pointer<RefStringTypeRhs>& rhs); |
| |
| /// @brief compares two JSON pointer for less-than |
| template<typename RefStringTypeLhs, typename RefStringTypeRhs> |
| // NOLINTNEXTLINE(readability-redundant-declaration) |
| friend bool operator<(const json_pointer<RefStringTypeLhs>& lhs, const json_pointer<RefStringTypeRhs>& rhs) noexcept; |
| #endif |
| |
| private: |
| /// the reference tokens |
| std::vector<string_t> reference_tokens; |
| }; |
| |
| #if !JSON_HAS_THREE_WAY_COMPARISON |
| // functions cannot be defined inside class due to ODR violations |
| template<typename RefStringTypeLhs, typename RefStringTypeRhs> |
| inline bool operator==(const json_pointer<RefStringTypeLhs>& lhs, const json_pointer<RefStringTypeRhs>& rhs) noexcept |
| { |
| return lhs.reference_tokens == rhs.reference_tokens; |
| } |
| |
| template<typename RefStringTypeLhs, typename StringType = typename json_pointer<RefStringTypeLhs>::string_t> |
| JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer)) |
| inline bool operator==(const json_pointer<RefStringTypeLhs>& lhs, const StringType& rhs) |
| { |
| return lhs == json_pointer<RefStringTypeLhs>(rhs); |
| } |
| |
| template<typename RefStringTypeRhs, typename StringType = typename json_pointer<RefStringTypeRhs>::string_t> |
| JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer)) |
| inline bool operator==(const StringType& lhs, const json_pointer<RefStringTypeRhs>& rhs) |
| { |
| return json_pointer<RefStringTypeRhs>(lhs) == rhs; |
| } |
| |
| template<typename RefStringTypeLhs, typename RefStringTypeRhs> |
| inline bool operator!=(const json_pointer<RefStringTypeLhs>& lhs, const json_pointer<RefStringTypeRhs>& rhs) noexcept |
| { |
| return !(lhs == rhs); |
| } |
| |
| template<typename RefStringTypeLhs, typename StringType = typename json_pointer<RefStringTypeLhs>::string_t> |
| JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer)) |
| inline bool operator!=(const json_pointer<RefStringTypeLhs>& lhs, const StringType& rhs) |
| { |
| return !(lhs == rhs); |
| } |
| |
| template<typename RefStringTypeRhs, typename StringType = typename json_pointer<RefStringTypeRhs>::string_t> |
| JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer)) |
| inline bool operator!=(const StringType& lhs, const json_pointer<RefStringTypeRhs>& rhs) |
| { |
| return !(lhs == rhs); |
| } |
| |
| template<typename RefStringTypeLhs, typename RefStringTypeRhs> |
| inline bool operator<(const json_pointer<RefStringTypeLhs>& lhs, const json_pointer<RefStringTypeRhs>& rhs) noexcept |
| { |
| return lhs.reference_tokens < rhs.reference_tokens; |
| } |
| #endif |
| |
| NLOHMANN_JSON_NAMESPACE_END |