unit-json_pointer.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  1. /*
  2. __ _____ _____ _____
  3. __| | __| | | | JSON for Modern C++ (test suite)
  4. | | |__ | | | | | | version 3.7.3
  5. |_____|_____|_____|_|___| https://github.com/nlohmann/json
  6. Licensed under the MIT License <http://opensource.org/licenses/MIT>.
  7. SPDX-License-Identifier: MIT
  8. Copyright (c) 2013-2019 Niels Lohmann <http://nlohmann.me>.
  9. Permission is hereby granted, free of charge, to any person obtaining a copy
  10. of this software and associated documentation files (the "Software"), to deal
  11. in the Software without restriction, including without limitation the rights
  12. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. copies of the Software, and to permit persons to whom the Software is
  14. furnished to do so, subject to the following conditions:
  15. The above copyright notice and this permission notice shall be included in all
  16. copies or substantial portions of the Software.
  17. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  23. SOFTWARE.
  24. */
  25. #include "doctest_compatibility.h"
  26. #define private public
  27. #include <nlohmann/json.hpp>
  28. using nlohmann::json;
  29. #undef private
  30. TEST_CASE("JSON pointers")
  31. {
  32. SECTION("errors")
  33. {
  34. CHECK_THROWS_AS(json::json_pointer("foo"), json::parse_error&);
  35. CHECK_THROWS_WITH(json::json_pointer("foo"),
  36. "[json.exception.parse_error.107] parse error at byte 1: JSON pointer must be empty or begin with '/' - was: 'foo'");
  37. CHECK_THROWS_AS(json::json_pointer("/~~"), json::parse_error&);
  38. CHECK_THROWS_WITH(json::json_pointer("/~~"),
  39. "[json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1'");
  40. CHECK_THROWS_AS(json::json_pointer("/~"), json::parse_error&);
  41. CHECK_THROWS_WITH(json::json_pointer("/~"),
  42. "[json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1'");
  43. json::json_pointer p;
  44. CHECK_THROWS_AS(p.top(), json::out_of_range&);
  45. CHECK_THROWS_WITH(p.top(),
  46. "[json.exception.out_of_range.405] JSON pointer has no parent");
  47. CHECK_THROWS_AS(p.pop_back(), json::out_of_range&);
  48. CHECK_THROWS_WITH(p.pop_back(),
  49. "[json.exception.out_of_range.405] JSON pointer has no parent");
  50. SECTION("array index error")
  51. {
  52. json v = {1, 2, 3, 4};
  53. json::json_pointer ptr("/10e");
  54. CHECK_THROWS_AS(v[ptr], json::out_of_range&);
  55. CHECK_THROWS_WITH(v[ptr],
  56. "[json.exception.out_of_range.404] unresolved reference token '10e'");
  57. }
  58. }
  59. SECTION("examples from RFC 6901")
  60. {
  61. SECTION("nonconst access")
  62. {
  63. json j = R"(
  64. {
  65. "foo": ["bar", "baz"],
  66. "": 0,
  67. "a/b": 1,
  68. "c%d": 2,
  69. "e^f": 3,
  70. "g|h": 4,
  71. "i\\j": 5,
  72. "k\"l": 6,
  73. " ": 7,
  74. "m~n": 8
  75. }
  76. )"_json;
  77. // the whole document
  78. CHECK(j[json::json_pointer()] == j);
  79. CHECK(j[json::json_pointer("")] == j);
  80. CHECK(j.contains(json::json_pointer()));
  81. CHECK(j.contains(json::json_pointer("")));
  82. // array access
  83. CHECK(j[json::json_pointer("/foo")] == j["foo"]);
  84. CHECK(j.contains(json::json_pointer("/foo")));
  85. CHECK(j[json::json_pointer("/foo/0")] == j["foo"][0]);
  86. CHECK(j[json::json_pointer("/foo/1")] == j["foo"][1]);
  87. CHECK(j["/foo/1"_json_pointer] == j["foo"][1]);
  88. CHECK(j.contains(json::json_pointer("/foo/0")));
  89. CHECK(j.contains(json::json_pointer("/foo/1")));
  90. CHECK(not j.contains(json::json_pointer("/foo/3")));
  91. CHECK(not j.contains(json::json_pointer("/foo/+")));
  92. CHECK(not j.contains(json::json_pointer("/foo/1+2")));
  93. CHECK(not j.contains(json::json_pointer("/foo/-")));
  94. // checked array access
  95. CHECK(j.at(json::json_pointer("/foo/0")) == j["foo"][0]);
  96. CHECK(j.at(json::json_pointer("/foo/1")) == j["foo"][1]);
  97. // empty string access
  98. CHECK(j[json::json_pointer("/")] == j[""]);
  99. CHECK(j.contains(json::json_pointer("")));
  100. CHECK(j.contains(json::json_pointer("/")));
  101. // other cases
  102. CHECK(j[json::json_pointer("/ ")] == j[" "]);
  103. CHECK(j[json::json_pointer("/c%d")] == j["c%d"]);
  104. CHECK(j[json::json_pointer("/e^f")] == j["e^f"]);
  105. CHECK(j[json::json_pointer("/g|h")] == j["g|h"]);
  106. CHECK(j[json::json_pointer("/i\\j")] == j["i\\j"]);
  107. CHECK(j[json::json_pointer("/k\"l")] == j["k\"l"]);
  108. // contains
  109. CHECK(j.contains(json::json_pointer("/ ")));
  110. CHECK(j.contains(json::json_pointer("/c%d")));
  111. CHECK(j.contains(json::json_pointer("/e^f")));
  112. CHECK(j.contains(json::json_pointer("/g|h")));
  113. CHECK(j.contains(json::json_pointer("/i\\j")));
  114. CHECK(j.contains(json::json_pointer("/k\"l")));
  115. // checked access
  116. CHECK(j.at(json::json_pointer("/ ")) == j[" "]);
  117. CHECK(j.at(json::json_pointer("/c%d")) == j["c%d"]);
  118. CHECK(j.at(json::json_pointer("/e^f")) == j["e^f"]);
  119. CHECK(j.at(json::json_pointer("/g|h")) == j["g|h"]);
  120. CHECK(j.at(json::json_pointer("/i\\j")) == j["i\\j"]);
  121. CHECK(j.at(json::json_pointer("/k\"l")) == j["k\"l"]);
  122. // escaped access
  123. CHECK(j[json::json_pointer("/a~1b")] == j["a/b"]);
  124. CHECK(j[json::json_pointer("/m~0n")] == j["m~n"]);
  125. CHECK(j.contains(json::json_pointer("/a~1b")));
  126. CHECK(j.contains(json::json_pointer("/m~0n")));
  127. // unescaped access
  128. // access to nonexisting values yield object creation
  129. CHECK(not j.contains(json::json_pointer("/a/b")));
  130. CHECK_NOTHROW(j[json::json_pointer("/a/b")] = 42);
  131. CHECK(j.contains(json::json_pointer("/a/b")));
  132. CHECK(j["a"]["b"] == json(42));
  133. CHECK(not j.contains(json::json_pointer("/a/c/1")));
  134. CHECK_NOTHROW(j[json::json_pointer("/a/c/1")] = 42);
  135. CHECK(j["a"]["c"] == json({nullptr, 42}));
  136. CHECK(j.contains(json::json_pointer("/a/c/1")));
  137. CHECK(not j.contains(json::json_pointer("/a/d/-")));
  138. CHECK_NOTHROW(j[json::json_pointer("/a/d/-")] = 42);
  139. CHECK(not j.contains(json::json_pointer("/a/d/-")));
  140. CHECK(j["a"]["d"] == json::array({42}));
  141. // "/a/b" works for JSON {"a": {"b": 42}}
  142. CHECK(json({{"a", {{"b", 42}}}})[json::json_pointer("/a/b")] == json(42));
  143. // unresolved access
  144. json j_primitive = 1;
  145. CHECK_THROWS_AS(j_primitive["/foo"_json_pointer], json::out_of_range&);
  146. CHECK_THROWS_WITH(j_primitive["/foo"_json_pointer],
  147. "[json.exception.out_of_range.404] unresolved reference token 'foo'");
  148. CHECK_THROWS_AS(j_primitive.at("/foo"_json_pointer), json::out_of_range&);
  149. CHECK_THROWS_WITH(j_primitive.at("/foo"_json_pointer),
  150. "[json.exception.out_of_range.404] unresolved reference token 'foo'");
  151. CHECK(not j_primitive.contains(json::json_pointer("/foo")));
  152. }
  153. SECTION("const access")
  154. {
  155. const json j = R"(
  156. {
  157. "foo": ["bar", "baz"],
  158. "": 0,
  159. "a/b": 1,
  160. "c%d": 2,
  161. "e^f": 3,
  162. "g|h": 4,
  163. "i\\j": 5,
  164. "k\"l": 6,
  165. " ": 7,
  166. "m~n": 8
  167. }
  168. )"_json;
  169. // the whole document
  170. CHECK(j[json::json_pointer()] == j);
  171. CHECK(j[json::json_pointer("")] == j);
  172. // array access
  173. CHECK(j[json::json_pointer("/foo")] == j["foo"]);
  174. CHECK(j[json::json_pointer("/foo/0")] == j["foo"][0]);
  175. CHECK(j[json::json_pointer("/foo/1")] == j["foo"][1]);
  176. CHECK(j["/foo/1"_json_pointer] == j["foo"][1]);
  177. // checked array access
  178. CHECK(j.at(json::json_pointer("/foo/0")) == j["foo"][0]);
  179. CHECK(j.at(json::json_pointer("/foo/1")) == j["foo"][1]);
  180. // empty string access
  181. CHECK(j[json::json_pointer("/")] == j[""]);
  182. // other cases
  183. CHECK(j[json::json_pointer("/ ")] == j[" "]);
  184. CHECK(j[json::json_pointer("/c%d")] == j["c%d"]);
  185. CHECK(j[json::json_pointer("/e^f")] == j["e^f"]);
  186. CHECK(j[json::json_pointer("/g|h")] == j["g|h"]);
  187. CHECK(j[json::json_pointer("/i\\j")] == j["i\\j"]);
  188. CHECK(j[json::json_pointer("/k\"l")] == j["k\"l"]);
  189. // checked access
  190. CHECK(j.at(json::json_pointer("/ ")) == j[" "]);
  191. CHECK(j.at(json::json_pointer("/c%d")) == j["c%d"]);
  192. CHECK(j.at(json::json_pointer("/e^f")) == j["e^f"]);
  193. CHECK(j.at(json::json_pointer("/g|h")) == j["g|h"]);
  194. CHECK(j.at(json::json_pointer("/i\\j")) == j["i\\j"]);
  195. CHECK(j.at(json::json_pointer("/k\"l")) == j["k\"l"]);
  196. // escaped access
  197. CHECK(j[json::json_pointer("/a~1b")] == j["a/b"]);
  198. CHECK(j[json::json_pointer("/m~0n")] == j["m~n"]);
  199. // unescaped access
  200. CHECK_THROWS_AS(j.at(json::json_pointer("/a/b")), json::out_of_range&);
  201. CHECK_THROWS_WITH(j.at(json::json_pointer("/a/b")),
  202. "[json.exception.out_of_range.403] key 'a' not found");
  203. // unresolved access
  204. const json j_primitive = 1;
  205. CHECK_THROWS_AS(j_primitive["/foo"_json_pointer], json::out_of_range&);
  206. CHECK_THROWS_WITH(j_primitive["/foo"_json_pointer],
  207. "[json.exception.out_of_range.404] unresolved reference token 'foo'");
  208. CHECK_THROWS_AS(j_primitive.at("/foo"_json_pointer), json::out_of_range&);
  209. CHECK_THROWS_WITH(j_primitive.at("/foo"_json_pointer),
  210. "[json.exception.out_of_range.404] unresolved reference token 'foo'");
  211. }
  212. SECTION("user-defined string literal")
  213. {
  214. json j = R"(
  215. {
  216. "foo": ["bar", "baz"],
  217. "": 0,
  218. "a/b": 1,
  219. "c%d": 2,
  220. "e^f": 3,
  221. "g|h": 4,
  222. "i\\j": 5,
  223. "k\"l": 6,
  224. " ": 7,
  225. "m~n": 8
  226. }
  227. )"_json;
  228. // the whole document
  229. CHECK(j[""_json_pointer] == j);
  230. CHECK(j.contains(""_json_pointer));
  231. // array access
  232. CHECK(j["/foo"_json_pointer] == j["foo"]);
  233. CHECK(j["/foo/0"_json_pointer] == j["foo"][0]);
  234. CHECK(j["/foo/1"_json_pointer] == j["foo"][1]);
  235. CHECK(j.contains("/foo"_json_pointer));
  236. CHECK(j.contains("/foo/0"_json_pointer));
  237. CHECK(j.contains("/foo/1"_json_pointer));
  238. CHECK(not j.contains("/foo/-"_json_pointer));
  239. }
  240. }
  241. SECTION("array access")
  242. {
  243. SECTION("nonconst access")
  244. {
  245. json j = {1, 2, 3};
  246. const json j_const = j;
  247. // check reading access
  248. CHECK(j["/0"_json_pointer] == j[0]);
  249. CHECK(j["/1"_json_pointer] == j[1]);
  250. CHECK(j["/2"_json_pointer] == j[2]);
  251. // assign to existing index
  252. j["/1"_json_pointer] = 13;
  253. CHECK(j[1] == json(13));
  254. // assign to nonexisting index
  255. j["/3"_json_pointer] = 33;
  256. CHECK(j[3] == json(33));
  257. // assign to nonexisting index (with gap)
  258. j["/5"_json_pointer] = 55;
  259. CHECK(j == json({1, 13, 3, 33, nullptr, 55}));
  260. // error with leading 0
  261. CHECK_THROWS_AS(j["/01"_json_pointer], json::parse_error&);
  262. CHECK_THROWS_WITH(j["/01"_json_pointer],
  263. "[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'");
  264. CHECK_THROWS_AS(j_const["/01"_json_pointer], json::parse_error&);
  265. CHECK_THROWS_WITH(j_const["/01"_json_pointer],
  266. "[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'");
  267. CHECK_THROWS_AS(j.at("/01"_json_pointer), json::parse_error&);
  268. CHECK_THROWS_WITH(j.at("/01"_json_pointer),
  269. "[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'");
  270. CHECK_THROWS_AS(j_const.at("/01"_json_pointer), json::parse_error&);
  271. CHECK_THROWS_WITH(j_const.at("/01"_json_pointer),
  272. "[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'");
  273. CHECK(not j.contains("/01"_json_pointer));
  274. CHECK(not j.contains("/01"_json_pointer));
  275. CHECK(not j_const.contains("/01"_json_pointer));
  276. CHECK(not j_const.contains("/01"_json_pointer));
  277. // error with incorrect numbers
  278. CHECK_THROWS_AS(j["/one"_json_pointer] = 1, json::parse_error&);
  279. CHECK_THROWS_WITH(j["/one"_json_pointer] = 1,
  280. "[json.exception.parse_error.109] parse error: array index 'one' is not a number");
  281. CHECK_THROWS_AS(j_const["/one"_json_pointer] == 1, json::parse_error&);
  282. CHECK_THROWS_WITH(j_const["/one"_json_pointer] == 1,
  283. "[json.exception.parse_error.109] parse error: array index 'one' is not a number");
  284. CHECK_THROWS_AS(j.at("/one"_json_pointer) = 1, json::parse_error&);
  285. CHECK_THROWS_WITH(j.at("/one"_json_pointer) = 1,
  286. "[json.exception.parse_error.109] parse error: array index 'one' is not a number");
  287. CHECK_THROWS_AS(j_const.at("/one"_json_pointer) == 1, json::parse_error&);
  288. CHECK_THROWS_WITH(j_const.at("/one"_json_pointer) == 1,
  289. "[json.exception.parse_error.109] parse error: array index 'one' is not a number");
  290. CHECK_THROWS_AS(j["/+1"_json_pointer] = 1, json::parse_error&);
  291. CHECK_THROWS_WITH(j["/+1"_json_pointer] = 1,
  292. "[json.exception.parse_error.109] parse error: array index '+1' is not a number");
  293. CHECK_THROWS_AS(j_const["/+1"_json_pointer] == 1, json::parse_error&);
  294. CHECK_THROWS_WITH(j_const["/+1"_json_pointer] == 1,
  295. "[json.exception.parse_error.109] parse error: array index '+1' is not a number");
  296. CHECK_THROWS_AS(j["/1+1"_json_pointer] = 1, json::out_of_range&);
  297. CHECK_THROWS_WITH(j["/1+1"_json_pointer] = 1,
  298. "[json.exception.out_of_range.404] unresolved reference token '1+1'");
  299. CHECK_THROWS_AS(j_const["/1+1"_json_pointer] == 1, json::out_of_range&);
  300. CHECK_THROWS_WITH(j_const["/1+1"_json_pointer] == 1,
  301. "[json.exception.out_of_range.404] unresolved reference token '1+1'");
  302. CHECK_THROWS_AS(j["/111111111111111111111111"_json_pointer] = 1, json::out_of_range&);
  303. CHECK_THROWS_WITH(j["/111111111111111111111111"_json_pointer] = 1,
  304. "[json.exception.out_of_range.404] unresolved reference token '111111111111111111111111'");
  305. CHECK_THROWS_AS(j_const["/111111111111111111111111"_json_pointer] == 1, json::out_of_range&);
  306. CHECK_THROWS_WITH(j_const["/111111111111111111111111"_json_pointer] == 1,
  307. "[json.exception.out_of_range.404] unresolved reference token '111111111111111111111111'");
  308. CHECK_THROWS_AS(j.at("/one"_json_pointer) = 1, json::parse_error&);
  309. CHECK_THROWS_WITH(j.at("/one"_json_pointer) = 1,
  310. "[json.exception.parse_error.109] parse error: array index 'one' is not a number");
  311. CHECK_THROWS_AS(j_const.at("/one"_json_pointer) == 1, json::parse_error&);
  312. CHECK_THROWS_WITH(j_const.at("/one"_json_pointer) == 1,
  313. "[json.exception.parse_error.109] parse error: array index 'one' is not a number");
  314. CHECK(not j.contains("/one"_json_pointer));
  315. CHECK(not j.contains("/one"_json_pointer));
  316. CHECK(not j_const.contains("/one"_json_pointer));
  317. CHECK(not j_const.contains("/one"_json_pointer));
  318. CHECK_THROWS_AS(json({{"/list/0", 1}, {"/list/1", 2}, {"/list/three", 3}}).unflatten(), json::parse_error&);
  319. CHECK_THROWS_WITH(json({{"/list/0", 1}, {"/list/1", 2}, {"/list/three", 3}}).unflatten(),
  320. "[json.exception.parse_error.109] parse error: array index 'three' is not a number");
  321. // assign to "-"
  322. j["/-"_json_pointer] = 99;
  323. CHECK(j == json({1, 13, 3, 33, nullptr, 55, 99}));
  324. // error when using "-" in const object
  325. CHECK_THROWS_AS(j_const["/-"_json_pointer], json::out_of_range&);
  326. CHECK_THROWS_WITH(j_const["/-"_json_pointer],
  327. "[json.exception.out_of_range.402] array index '-' (3) is out of range");
  328. CHECK(not j_const.contains("/-"_json_pointer));
  329. // error when using "-" with at
  330. CHECK_THROWS_AS(j.at("/-"_json_pointer), json::out_of_range&);
  331. CHECK_THROWS_WITH(j.at("/-"_json_pointer),
  332. "[json.exception.out_of_range.402] array index '-' (7) is out of range");
  333. CHECK_THROWS_AS(j_const.at("/-"_json_pointer), json::out_of_range&);
  334. CHECK_THROWS_WITH(j_const.at("/-"_json_pointer),
  335. "[json.exception.out_of_range.402] array index '-' (3) is out of range");
  336. CHECK(not j_const.contains("/-"_json_pointer));
  337. }
  338. SECTION("const access")
  339. {
  340. const json j = {1, 2, 3};
  341. // check reading access
  342. CHECK(j["/0"_json_pointer] == j[0]);
  343. CHECK(j["/1"_json_pointer] == j[1]);
  344. CHECK(j["/2"_json_pointer] == j[2]);
  345. // assign to nonexisting index
  346. CHECK_THROWS_AS(j.at("/3"_json_pointer), json::out_of_range&);
  347. CHECK_THROWS_WITH(j.at("/3"_json_pointer),
  348. "[json.exception.out_of_range.401] array index 3 is out of range");
  349. CHECK(not j.contains("/3"_json_pointer));
  350. // assign to nonexisting index (with gap)
  351. CHECK_THROWS_AS(j.at("/5"_json_pointer), json::out_of_range&);
  352. CHECK_THROWS_WITH(j.at("/5"_json_pointer),
  353. "[json.exception.out_of_range.401] array index 5 is out of range");
  354. CHECK(not j.contains("/5"_json_pointer));
  355. // assign to "-"
  356. CHECK_THROWS_AS(j["/-"_json_pointer], json::out_of_range&);
  357. CHECK_THROWS_WITH(j["/-"_json_pointer],
  358. "[json.exception.out_of_range.402] array index '-' (3) is out of range");
  359. CHECK_THROWS_AS(j.at("/-"_json_pointer), json::out_of_range&);
  360. CHECK_THROWS_WITH(j.at("/-"_json_pointer),
  361. "[json.exception.out_of_range.402] array index '-' (3) is out of range");
  362. CHECK(not j.contains("/-"_json_pointer));
  363. }
  364. }
  365. SECTION("flatten")
  366. {
  367. json j =
  368. {
  369. {"pi", 3.141},
  370. {"happy", true},
  371. {"name", "Niels"},
  372. {"nothing", nullptr},
  373. {
  374. "answer", {
  375. {"everything", 42}
  376. }
  377. },
  378. {"list", {1, 0, 2}},
  379. {
  380. "object", {
  381. {"currency", "USD"},
  382. {"value", 42.99},
  383. {"", "empty string"},
  384. {"/", "slash"},
  385. {"~", "tilde"},
  386. {"~1", "tilde1"}
  387. }
  388. }
  389. };
  390. json j_flatten =
  391. {
  392. {"/pi", 3.141},
  393. {"/happy", true},
  394. {"/name", "Niels"},
  395. {"/nothing", nullptr},
  396. {"/answer/everything", 42},
  397. {"/list/0", 1},
  398. {"/list/1", 0},
  399. {"/list/2", 2},
  400. {"/object/currency", "USD"},
  401. {"/object/value", 42.99},
  402. {"/object/", "empty string"},
  403. {"/object/~1", "slash"},
  404. {"/object/~0", "tilde"},
  405. {"/object/~01", "tilde1"}
  406. };
  407. // check if flattened result is as expected
  408. CHECK(j.flatten() == j_flatten);
  409. // check if unflattened result is as expected
  410. CHECK(j_flatten.unflatten() == j);
  411. // error for nonobjects
  412. CHECK_THROWS_AS(json(1).unflatten(), json::type_error&);
  413. CHECK_THROWS_WITH(json(1).unflatten(),
  414. "[json.exception.type_error.314] only objects can be unflattened");
  415. // error for nonprimitve values
  416. CHECK_THROWS_AS(json({{"/1", {1, 2, 3}}}).unflatten(), json::type_error&);
  417. CHECK_THROWS_WITH(json({{"/1", {1, 2, 3}}}).unflatten(),
  418. "[json.exception.type_error.315] values in object must be primitive");
  419. // error for conflicting values
  420. json j_error = {{"", 42}, {"/foo", 17}};
  421. CHECK_THROWS_AS(j_error.unflatten(), json::type_error&);
  422. CHECK_THROWS_WITH(j_error.unflatten(),
  423. "[json.exception.type_error.313] invalid value to unflatten");
  424. // explicit roundtrip check
  425. CHECK(j.flatten().unflatten() == j);
  426. // roundtrip for primitive values
  427. json j_null;
  428. CHECK(j_null.flatten().unflatten() == j_null);
  429. json j_number = 42;
  430. CHECK(j_number.flatten().unflatten() == j_number);
  431. json j_boolean = false;
  432. CHECK(j_boolean.flatten().unflatten() == j_boolean);
  433. json j_string = "foo";
  434. CHECK(j_string.flatten().unflatten() == j_string);
  435. // roundtrip for empty structured values (will be unflattened to null)
  436. json j_array(json::value_t::array);
  437. CHECK(j_array.flatten().unflatten() == json());
  438. json j_object(json::value_t::object);
  439. CHECK(j_object.flatten().unflatten() == json());
  440. }
  441. SECTION("string representation")
  442. {
  443. for (auto ptr :
  444. {"", "/foo", "/foo/0", "/", "/a~1b", "/c%d", "/e^f", "/g|h", "/i\\j", "/k\"l", "/ ", "/m~0n"
  445. })
  446. {
  447. CHECK(json::json_pointer(ptr).to_string() == ptr);
  448. CHECK(std::string(json::json_pointer(ptr)) == ptr);
  449. }
  450. }
  451. SECTION("conversion")
  452. {
  453. SECTION("array")
  454. {
  455. json j;
  456. // all numbers -> array
  457. j["/12"_json_pointer] = 0;
  458. CHECK(j.is_array());
  459. }
  460. SECTION("object")
  461. {
  462. json j;
  463. // contains a number, but is not a number -> object
  464. j["/a12"_json_pointer] = 0;
  465. CHECK(j.is_object());
  466. }
  467. }
  468. SECTION("empty, push, pop and parent")
  469. {
  470. const json j =
  471. {
  472. {"", "Hello"},
  473. {"pi", 3.141},
  474. {"happy", true},
  475. {"name", "Niels"},
  476. {"nothing", nullptr},
  477. {
  478. "answer", {
  479. {"everything", 42}
  480. }
  481. },
  482. {"list", {1, 0, 2}},
  483. {
  484. "object", {
  485. {"currency", "USD"},
  486. {"value", 42.99},
  487. {"", "empty string"},
  488. {"/", "slash"},
  489. {"~", "tilde"},
  490. {"~1", "tilde1"}
  491. }
  492. }
  493. };
  494. // empty json_pointer returns the root JSON-object
  495. auto ptr = ""_json_pointer;
  496. CHECK(ptr.empty());
  497. CHECK(j[ptr] == j);
  498. // simple field access
  499. ptr.push_back("pi");
  500. CHECK(!ptr.empty());
  501. CHECK(j[ptr] == j["pi"]);
  502. ptr.pop_back();
  503. CHECK(ptr.empty());
  504. CHECK(j[ptr] == j);
  505. // object and children access
  506. const std::string answer("answer");
  507. ptr.push_back(answer);
  508. ptr.push_back("everything");
  509. CHECK(!ptr.empty());
  510. CHECK(j[ptr] == j["answer"]["everything"]);
  511. // check access via const pointer
  512. const auto cptr = ptr;
  513. CHECK(cptr.back() == "everything");
  514. ptr.pop_back();
  515. ptr.pop_back();
  516. CHECK(ptr.empty());
  517. CHECK(j[ptr] == j);
  518. // push key which has to be encoded
  519. ptr.push_back("object");
  520. ptr.push_back("/");
  521. CHECK(j[ptr] == j["object"]["/"]);
  522. CHECK(ptr.to_string() == "/object/~1");
  523. CHECK(j[ptr.parent_pointer()] == j["object"]);
  524. ptr = ptr.parent_pointer().parent_pointer();
  525. CHECK(ptr.empty());
  526. CHECK(j[ptr] == j);
  527. // parent-pointer of the empty json_pointer is empty
  528. ptr = ptr.parent_pointer();
  529. CHECK(ptr.empty());
  530. CHECK(j[ptr] == j);
  531. CHECK_THROWS_WITH(ptr.pop_back(),
  532. "[json.exception.out_of_range.405] JSON pointer has no parent");
  533. }
  534. SECTION("operators")
  535. {
  536. const json j =
  537. {
  538. {"", "Hello"},
  539. {"pi", 3.141},
  540. {"happy", true},
  541. {"name", "Niels"},
  542. {"nothing", nullptr},
  543. {
  544. "answer", {
  545. {"everything", 42}
  546. }
  547. },
  548. {"list", {1, 0, 2}},
  549. {
  550. "object", {
  551. {"currency", "USD"},
  552. {"value", 42.99},
  553. {"", "empty string"},
  554. {"/", "slash"},
  555. {"~", "tilde"},
  556. {"~1", "tilde1"}
  557. }
  558. }
  559. };
  560. // empty json_pointer returns the root JSON-object
  561. auto ptr = ""_json_pointer;
  562. CHECK(j[ptr] == j);
  563. // simple field access
  564. ptr = ptr / "pi";
  565. CHECK(j[ptr] == j["pi"]);
  566. ptr.pop_back();
  567. CHECK(j[ptr] == j);
  568. // object and children access
  569. const std::string answer("answer");
  570. ptr /= answer;
  571. ptr = ptr / "everything";
  572. CHECK(j[ptr] == j["answer"]["everything"]);
  573. ptr.pop_back();
  574. ptr.pop_back();
  575. CHECK(j[ptr] == j);
  576. CHECK(ptr / ""_json_pointer == ptr);
  577. CHECK(j["/answer"_json_pointer / "/everything"_json_pointer] == j["answer"]["everything"]);
  578. // list children access
  579. CHECK(j["/list"_json_pointer / 1] == j["list"][1]);
  580. // push key which has to be encoded
  581. ptr /= "object";
  582. ptr = ptr / "/";
  583. CHECK(j[ptr] == j["object"]["/"]);
  584. CHECK(ptr.to_string() == "/object/~1");
  585. }
  586. }