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