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