123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971 |
- #pragma once
- #include <algorithm> // all_of
- #include <cassert> // assert
- #include <cctype> // isdigit
- #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/value_t.hpp>
- namespace nlohmann
- {
- template<typename BasicJsonType>
- class json_pointer
- {
- // allow basic_json to access private members
- NLOHMANN_BASIC_JSON_TPL_DECLARATION
- friend class basic_json;
- public:
- /*!
- @brief create JSON pointer
- Create a JSON pointer according to the syntax described in
- [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3).
- @param[in] s string representing the JSON pointer; if omitted, the empty
- string is assumed which references the whole JSON value
- @throw parse_error.107 if the given JSON pointer @a s is nonempty and does
- not begin with a slash (`/`); see example below
- @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is
- not followed by `0` (representing `~`) or `1` (representing `/`); see
- example below
- @liveexample{The example shows the construction several valid JSON pointers
- as well as the exceptional behavior.,json_pointer}
- @since version 2.0.0
- */
- explicit json_pointer(const std::string& s = "")
- : reference_tokens(split(s))
- {}
- /*!
- @brief return a string representation of the JSON pointer
- @invariant For each JSON pointer `ptr`, it holds:
- @code {.cpp}
- ptr == json_pointer(ptr.to_string());
- @endcode
- @return a string representation of the JSON pointer
- @liveexample{The example shows the result of `to_string`.,json_pointer__to_string}
- @since version 2.0.0
- */
- std::string to_string() const
- {
- return std::accumulate(reference_tokens.begin(), reference_tokens.end(),
- std::string{},
- [](const std::string & a, const std::string & b)
- {
- return a + "/" + escape(b);
- });
- }
- /// @copydoc to_string()
- operator std::string() const
- {
- return to_string();
- }
- /*!
- @brief append another JSON pointer at the end of this JSON pointer
- @param[in] ptr JSON pointer to append
- @return JSON pointer with @a ptr appended
- @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add}
- @complexity Linear in the length of @a ptr.
- @sa @ref operator/=(std::string) to append a reference token
- @sa @ref operator/=(std::size_t) to append an array index
- @sa @ref operator/(const json_pointer&, const json_pointer&) for a binary operator
- @since version 3.6.0
- */
- 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
- @param[in] token reference token to append
- @return JSON pointer with @a token appended without escaping @a token
- @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add}
- @complexity Amortized constant.
- @sa @ref operator/=(const json_pointer&) to append a JSON pointer
- @sa @ref operator/=(std::size_t) to append an array index
- @sa @ref operator/(const json_pointer&, std::size_t) for a binary operator
- @since version 3.6.0
- */
- json_pointer& operator/=(std::string token)
- {
- push_back(std::move(token));
- return *this;
- }
- /*!
- @brief append an array index at the end of this JSON pointer
- @param[in] array_idx array index to append
- @return JSON pointer with @a array_idx appended
- @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add}
- @complexity Amortized constant.
- @sa @ref operator/=(const json_pointer&) to append a JSON pointer
- @sa @ref operator/=(std::string) to append a reference token
- @sa @ref operator/(const json_pointer&, std::string) for a binary operator
- @since version 3.6.0
- */
- 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
- @param[in] lhs JSON pointer
- @param[in] rhs JSON pointer
- @return a new JSON pointer with @a rhs appended to @a lhs
- @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary}
- @complexity Linear in the length of @a lhs and @a rhs.
- @sa @ref operator/=(const json_pointer&) to append a JSON pointer
- @since version 3.6.0
- */
- 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
- @param[in] ptr JSON pointer
- @param[in] token reference token
- @return a new JSON pointer with unescaped @a token appended to @a ptr
- @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary}
- @complexity Linear in the length of @a ptr.
- @sa @ref operator/=(std::string) to append a reference token
- @since version 3.6.0
- */
- friend json_pointer operator/(const json_pointer& ptr, std::string token)
- {
- return json_pointer(ptr) /= std::move(token);
- }
- /*!
- @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer
- @param[in] ptr JSON pointer
- @param[in] array_idx array index
- @return a new JSON pointer with @a array_idx appended to @a ptr
- @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary}
- @complexity Linear in the length of @a ptr.
- @sa @ref operator/=(std::size_t) to append an array index
- @since version 3.6.0
- */
- friend json_pointer operator/(const json_pointer& ptr, std::size_t array_idx)
- {
- return json_pointer(ptr) /= array_idx;
- }
- /*!
- @brief returns the parent of this JSON pointer
- @return parent of this JSON pointer; in case this JSON pointer is the root,
- the root itself is returned
- @complexity Linear in the length of the JSON pointer.
- @liveexample{The example shows the result of `parent_pointer` for different
- JSON Pointers.,json_pointer__parent_pointer}
- @since version 3.6.0
- */
- json_pointer parent_pointer() const
- {
- if (empty())
- {
- return *this;
- }
- json_pointer res = *this;
- res.pop_back();
- return res;
- }
- /*!
- @brief remove last reference token
- @pre not `empty()`
- @liveexample{The example shows the usage of `pop_back`.,json_pointer__pop_back}
- @complexity Constant.
- @throw out_of_range.405 if JSON pointer has no parent
- @since version 3.6.0
- */
- void pop_back()
- {
- if (JSON_HEDLEY_UNLIKELY(empty()))
- {
- JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
- }
- reference_tokens.pop_back();
- }
- /*!
- @brief return last reference token
- @pre not `empty()`
- @return last reference token
- @liveexample{The example shows the usage of `back`.,json_pointer__back}
- @complexity Constant.
- @throw out_of_range.405 if JSON pointer has no parent
- @since version 3.6.0
- */
- const std::string& back() const
- {
- if (JSON_HEDLEY_UNLIKELY(empty()))
- {
- JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
- }
- return reference_tokens.back();
- }
- /*!
- @brief append an unescaped token at the end of the reference pointer
- @param[in] token token to add
- @complexity Amortized constant.
- @liveexample{The example shows the result of `push_back` for different
- JSON Pointers.,json_pointer__push_back}
- @since version 3.6.0
- */
- void push_back(const std::string& token)
- {
- reference_tokens.push_back(token);
- }
- /// @copydoc push_back(const std::string&)
- void push_back(std::string&& token)
- {
- reference_tokens.push_back(std::move(token));
- }
- /*!
- @brief return whether pointer points to the root document
- @return true iff the JSON pointer points to the root document
- @complexity Constant.
- @exceptionsafety No-throw guarantee: this function never throws exceptions.
- @liveexample{The example shows the result of `empty` for different JSON
- Pointers.,json_pointer__empty}
- @since version 3.6.0
- */
- 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 out_of_range.404 if string @a s could not be converted to an integer
- */
- static int array_index(const std::string& s)
- {
- // error condition (cf. RFC 6901, Sect. 4)
- if (JSON_HEDLEY_UNLIKELY(s.size() > 1 and s[0] == '0'))
- {
- JSON_THROW(detail::parse_error::create(106, 0,
- "array index '" + s +
- "' must not begin with '0'"));
- }
- // error condition (cf. RFC 6901, Sect. 4)
- if (JSON_HEDLEY_UNLIKELY(s.size() > 1 and not (s[0] >= '1' and s[0] <= '9')))
- {
- JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number"));
- }
- std::size_t processed_chars = 0;
- int res = 0;
- JSON_TRY
- {
- res = std::stoi(s, &processed_chars);
- }
- JSON_CATCH(std::out_of_range&)
- {
- JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'"));
- }
- // check if the string was completely read
- if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size()))
- {
- JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'"));
- }
- return res;
- }
- json_pointer top() const
- {
- if (JSON_HEDLEY_UNLIKELY(empty()))
- {
- JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
- }
- json_pointer result = *this;
- result.reference_tokens = {reference_tokens[0]};
- return result;
- }
- /*!
- @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
- */
- BasicJsonType& get_and_create(BasicJsonType& j) const
- {
- using size_type = typename BasicJsonType::size_type;
- 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[](static_cast<size_type>(array_index(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.
- */
- default:
- JSON_THROW(detail::type_error::create(313, "invalid value to unflatten"));
- }
- }
- 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
- */
- BasicJsonType& get_unchecked(BasicJsonType* ptr) const
- {
- using size_type = typename BasicJsonType::size_type;
- 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 or 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_value.array->size());
- }
- else
- {
- // convert array index to number; unchecked access
- ptr = &ptr->operator[](
- static_cast<size_type>(array_index(reference_token)));
- }
- break;
- }
- default:
- JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
- }
- }
- 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
- */
- BasicJsonType& get_checked(BasicJsonType* ptr) const
- {
- using size_type = typename BasicJsonType::size_type;
- 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,
- "array index '-' (" + std::to_string(ptr->m_value.array->size()) +
- ") is out of range"));
- }
- // note: at performs range check
- ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
- break;
- }
- default:
- JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
- }
- }
- 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
- */
- const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const
- {
- using size_type = typename BasicJsonType::size_type;
- 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,
- "array index '-' (" + std::to_string(ptr->m_value.array->size()) +
- ") is out of range"));
- }
- // use unchecked array access
- ptr = &ptr->operator[](
- static_cast<size_type>(array_index(reference_token)));
- break;
- }
- default:
- JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
- }
- }
- 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
- */
- const BasicJsonType& get_checked(const BasicJsonType* ptr) const
- {
- using size_type = typename BasicJsonType::size_type;
- 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,
- "array index '-' (" + std::to_string(ptr->m_value.array->size()) +
- ") is out of range"));
- }
- // note: at performs range check
- ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
- break;
- }
- default:
- JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
- }
- }
- 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
- */
- bool contains(const BasicJsonType* ptr) const
- {
- using size_type = typename BasicJsonType::size_type;
- for (const auto& reference_token : reference_tokens)
- {
- switch (ptr->type())
- {
- case detail::value_t::object:
- {
- if (not 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 and not ("0" <= reference_token and reference_token <= "9")))
- {
- // invalid char
- return false;
- }
- if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1))
- {
- if (JSON_HEDLEY_UNLIKELY(not ('1' <= reference_token[0] and 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(not ('0' <= reference_token[i] and reference_token[i] <= '9')))
- {
- // other char should be between '0' and '9'
- return false;
- }
- }
- }
- const auto idx = static_cast<size_type>(array_index(reference_token));
- if (idx >= ptr->size())
- {
- // index out of range
- return false;
- }
- ptr = &ptr->operator[](idx);
- break;
- }
- 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<std::string> split(const std::string& reference_string)
- {
- std::vector<std::string> 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,
- "JSON pointer must be empty or begin with '/' - was: '" +
- reference_string + "'"));
- }
- // 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 == std::string::npos)
- start != 0;
- // set the beginning of the next reference token
- // (will eventually be 0 if slash == std::string::npos)
- start = (slash == std::string::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 != std::string::npos;
- pos = reference_token.find_first_of('~', pos + 1))
- {
- assert(reference_token[pos] == '~');
- // ~ must be followed by 0 or 1
- if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 or
- (reference_token[pos + 1] != '0' and
- reference_token[pos + 1] != '1')))
- {
- JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'"));
- }
- }
- // finally, store the reference token
- unescape(reference_token);
- result.push_back(reference_token);
- }
- return result;
- }
- /*!
- @brief replace all occurrences of a substring by another string
- @param[in,out] s the string to manipulate; changed so that all
- occurrences of @a f are replaced with @a t
- @param[in] f the substring to replace with @a t
- @param[in] t the string to replace @a f
- @pre The search string @a f must not be empty. **This precondition is
- enforced with an assertion.**
- @since version 2.0.0
- */
- static void replace_substring(std::string& s, const std::string& f,
- const std::string& t)
- {
- assert(not f.empty());
- for (auto pos = s.find(f); // find first occurrence of f
- pos != std::string::npos; // make sure f was found
- s.replace(pos, f.size(), t), // replace with t, and
- pos = s.find(f, pos + t.size())) // find next occurrence of f
- {}
- }
- /// escape "~" to "~0" and "/" to "~1"
- static std::string escape(std::string s)
- {
- replace_substring(s, "~", "~0");
- replace_substring(s, "/", "~1");
- return s;
- }
- /// unescape "~1" to tilde and "~0" to slash (order is important!)
- static void unescape(std::string& s)
- {
- replace_substring(s, "~1", "/");
- replace_substring(s, "~0", "~");
- }
- /*!
- @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`.
- */
- static void flatten(const std::string& reference_string,
- const BasicJsonType& value,
- BasicJsonType& result)
- {
- switch (value.type())
- {
- case detail::value_t::array:
- {
- if (value.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_value.array->size(); ++i)
- {
- flatten(reference_string + "/" + std::to_string(i),
- value.m_value.array->operator[](i), result);
- }
- }
- break;
- }
- case detail::value_t::object:
- {
- if (value.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_value.object)
- {
- flatten(reference_string + "/" + escape(element.first), element.second, result);
- }
- }
- break;
- }
- 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
- */
- static BasicJsonType
- unflatten(const BasicJsonType& value)
- {
- if (JSON_HEDLEY_UNLIKELY(not value.is_object()))
- {
- JSON_THROW(detail::type_error::create(314, "only objects can be unflattened"));
- }
- BasicJsonType result;
- // iterate the JSON object values
- for (const auto& element : *value.m_value.object)
- {
- if (JSON_HEDLEY_UNLIKELY(not element.second.is_primitive()))
- {
- JSON_THROW(detail::type_error::create(315, "values in object must be primitive"));
- }
- // 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;
- }
- /*!
- @brief compares two JSON pointers for equality
- @param[in] lhs JSON pointer to compare
- @param[in] rhs JSON pointer to compare
- @return whether @a lhs is equal to @a rhs
- @complexity Linear in the length of the JSON pointer
- @exceptionsafety No-throw guarantee: this function never throws exceptions.
- */
- friend bool operator==(json_pointer const& lhs,
- json_pointer const& rhs) noexcept
- {
- return lhs.reference_tokens == rhs.reference_tokens;
- }
- /*!
- @brief compares two JSON pointers for inequality
- @param[in] lhs JSON pointer to compare
- @param[in] rhs JSON pointer to compare
- @return whether @a lhs is not equal @a rhs
- @complexity Linear in the length of the JSON pointer
- @exceptionsafety No-throw guarantee: this function never throws exceptions.
- */
- friend bool operator!=(json_pointer const& lhs,
- json_pointer const& rhs) noexcept
- {
- return not (lhs == rhs);
- }
- /// the reference tokens
- std::vector<std::string> reference_tokens;
- };
- } // namespace nlohmann
|