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