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