json_pointer.hpp 36 KB

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