• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/
to_string()80     JSON_HEDLEY_DEPRECATED_FOR(3.11.0, to_string())
81     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/
empty() const197     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:
252     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>
operator ==(const json_pointer<RefStringTypeRhs> & rhs) const854     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>
operator <=>(const json_pointer<RefStringTypeRhs> & rhs) const869     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>
operator ==(const json_pointer<RefStringTypeLhs> & lhs,const json_pointer<RefStringTypeRhs> & rhs)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))
operator ==(const json_pointer<RefStringTypeLhs> & lhs,const StringType & rhs)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))
operator ==(const StringType & lhs,const json_pointer<RefStringTypeRhs> & rhs)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>
operator !=(const json_pointer<RefStringTypeLhs> & lhs,const json_pointer<RefStringTypeRhs> & rhs)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))
operator !=(const json_pointer<RefStringTypeLhs> & lhs,const StringType & rhs)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))
operator !=(const StringType & lhs,const json_pointer<RefStringTypeRhs> & rhs)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>
operator <(const json_pointer<RefStringTypeLhs> & lhs,const json_pointer<RefStringTypeRhs> & rhs)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