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