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