1 // __ _____ _____ _____ 2 // __| | __| | | | JSON for Modern C++ (supporting code) 3 // | | |__ | | | | | | version 3.11.2 4 // |_____|_____|_____|_|___| https://github.com/nlohmann/json 5 // 6 // SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me> 7 // SPDX-License-Identifier: MIT 8 9 #include "doctest_compatibility.h" 10 11 #include <nlohmann/json.hpp> 12 using nlohmann::json; 13 14 #include <fstream> 15 #include <sstream> 16 #include "make_test_data_available.hpp" 17 18 TEST_CASE("object inspection") 19 { 20 SECTION("convenience type checker") 21 { 22 SECTION("object") 23 { 24 json j {{"foo", 1}, {"bar", false}}; 25 CHECK(!j.is_null()); 26 CHECK(!j.is_boolean()); 27 CHECK(!j.is_number()); 28 CHECK(!j.is_number_integer()); 29 CHECK(!j.is_number_unsigned()); 30 CHECK(!j.is_number_float()); 31 CHECK(!j.is_binary()); 32 CHECK(j.is_object()); 33 CHECK(!j.is_array()); 34 CHECK(!j.is_string()); 35 CHECK(!j.is_discarded()); 36 CHECK(!j.is_primitive()); 37 CHECK(j.is_structured()); 38 } 39 40 SECTION("array") 41 { 42 json j {"foo", 1, 1u, 42.23, false}; 43 CHECK(!j.is_null()); 44 CHECK(!j.is_boolean()); 45 CHECK(!j.is_number()); 46 CHECK(!j.is_number_integer()); 47 CHECK(!j.is_number_unsigned()); 48 CHECK(!j.is_number_float()); 49 CHECK(!j.is_binary()); 50 CHECK(!j.is_object()); 51 CHECK(j.is_array()); 52 CHECK(!j.is_string()); 53 CHECK(!j.is_discarded()); 54 CHECK(!j.is_primitive()); 55 CHECK(j.is_structured()); 56 } 57 58 SECTION("null") 59 { 60 json j(nullptr); 61 CHECK(j.is_null()); 62 CHECK(!j.is_boolean()); 63 CHECK(!j.is_number()); 64 CHECK(!j.is_number_integer()); 65 CHECK(!j.is_number_unsigned()); 66 CHECK(!j.is_number_float()); 67 CHECK(!j.is_binary()); 68 CHECK(!j.is_object()); 69 CHECK(!j.is_array()); 70 CHECK(!j.is_string()); 71 CHECK(!j.is_discarded()); 72 CHECK(j.is_primitive()); 73 CHECK(!j.is_structured()); 74 } 75 76 SECTION("boolean") 77 { 78 json j(true); 79 CHECK(!j.is_null()); 80 CHECK(j.is_boolean()); 81 CHECK(!j.is_number()); 82 CHECK(!j.is_number_integer()); 83 CHECK(!j.is_number_unsigned()); 84 CHECK(!j.is_number_float()); 85 CHECK(!j.is_binary()); 86 CHECK(!j.is_object()); 87 CHECK(!j.is_array()); 88 CHECK(!j.is_string()); 89 CHECK(!j.is_discarded()); 90 CHECK(j.is_primitive()); 91 CHECK(!j.is_structured()); 92 } 93 94 SECTION("string") 95 { 96 json j("Hello world"); 97 CHECK(!j.is_null()); 98 CHECK(!j.is_boolean()); 99 CHECK(!j.is_number()); 100 CHECK(!j.is_number_integer()); 101 CHECK(!j.is_number_unsigned()); 102 CHECK(!j.is_number_float()); 103 CHECK(!j.is_binary()); 104 CHECK(!j.is_object()); 105 CHECK(!j.is_array()); 106 CHECK(j.is_string()); 107 CHECK(!j.is_discarded()); 108 CHECK(j.is_primitive()); 109 CHECK(!j.is_structured()); 110 } 111 112 SECTION("number (integer)") 113 { 114 json j(42); 115 CHECK(!j.is_null()); 116 CHECK(!j.is_boolean()); 117 CHECK(j.is_number()); 118 CHECK(j.is_number_integer()); 119 CHECK(!j.is_number_unsigned()); 120 CHECK(!j.is_number_float()); 121 CHECK(!j.is_binary()); 122 CHECK(!j.is_object()); 123 CHECK(!j.is_array()); 124 CHECK(!j.is_string()); 125 CHECK(!j.is_discarded()); 126 CHECK(j.is_primitive()); 127 CHECK(!j.is_structured()); 128 } 129 130 SECTION("number (unsigned)") 131 { 132 json j(42u); 133 CHECK(!j.is_null()); 134 CHECK(!j.is_boolean()); 135 CHECK(j.is_number()); 136 CHECK(j.is_number_integer()); 137 CHECK(j.is_number_unsigned()); 138 CHECK(!j.is_number_float()); 139 CHECK(!j.is_binary()); 140 CHECK(!j.is_object()); 141 CHECK(!j.is_array()); 142 CHECK(!j.is_string()); 143 CHECK(!j.is_discarded()); 144 CHECK(j.is_primitive()); 145 CHECK(!j.is_structured()); 146 } 147 148 SECTION("number (floating-point)") 149 { 150 json j(42.23); 151 CHECK(!j.is_null()); 152 CHECK(!j.is_boolean()); 153 CHECK(j.is_number()); 154 CHECK(!j.is_number_integer()); 155 CHECK(!j.is_number_unsigned()); 156 CHECK(j.is_number_float()); 157 CHECK(!j.is_binary()); 158 CHECK(!j.is_object()); 159 CHECK(!j.is_array()); 160 CHECK(!j.is_string()); 161 CHECK(!j.is_discarded()); 162 CHECK(j.is_primitive()); 163 CHECK(!j.is_structured()); 164 } 165 166 SECTION("binary") 167 { 168 json j(json::value_t::binary); 169 CHECK(!j.is_null()); 170 CHECK(!j.is_boolean()); 171 CHECK(!j.is_number()); 172 CHECK(!j.is_number_integer()); 173 CHECK(!j.is_number_unsigned()); 174 CHECK(!j.is_number_float()); 175 CHECK(j.is_binary()); 176 CHECK(!j.is_object()); 177 CHECK(!j.is_array()); 178 CHECK(!j.is_string()); 179 CHECK(!j.is_discarded()); 180 CHECK(j.is_primitive()); 181 CHECK(!j.is_structured()); 182 } 183 184 SECTION("discarded") 185 { 186 json j(json::value_t::discarded); 187 CHECK(!j.is_null()); 188 CHECK(!j.is_boolean()); 189 CHECK(!j.is_number()); 190 CHECK(!j.is_number_integer()); 191 CHECK(!j.is_number_unsigned()); 192 CHECK(!j.is_number_float()); 193 CHECK(!j.is_binary()); 194 CHECK(!j.is_object()); 195 CHECK(!j.is_array()); 196 CHECK(!j.is_string()); 197 CHECK(j.is_discarded()); 198 CHECK(!j.is_primitive()); 199 CHECK(!j.is_structured()); 200 } 201 } 202 203 SECTION("serialization") 204 { 205 json j {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} }; 206 207 SECTION("no indent / indent=-1") 208 { 209 CHECK(j.dump() == 210 "{\"array\":[1,2,3,4],\"boolean\":false,\"null\":null,\"number\":42,\"object\":{},\"string\":\"Hello world\"}"); 211 212 CHECK(j.dump() == j.dump(-1)); 213 } 214 215 SECTION("indent=0") 216 { 217 CHECK(j.dump(0) == 218 "{\n\"array\": [\n1,\n2,\n3,\n4\n],\n\"boolean\": false,\n\"null\": null,\n\"number\": 42,\n\"object\": {},\n\"string\": \"Hello world\"\n}"); 219 } 220 221 SECTION("indent=1, space='\t'") 222 { 223 CHECK(j.dump(1, '\t') == 224 "{\n\t\"array\": [\n\t\t1,\n\t\t2,\n\t\t3,\n\t\t4\n\t],\n\t\"boolean\": false,\n\t\"null\": null,\n\t\"number\": 42,\n\t\"object\": {},\n\t\"string\": \"Hello world\"\n}"); 225 } 226 227 SECTION("indent=4") 228 { 229 CHECK(j.dump(4) == 230 "{\n \"array\": [\n 1,\n 2,\n 3,\n 4\n ],\n \"boolean\": false,\n \"null\": null,\n \"number\": 42,\n \"object\": {},\n \"string\": \"Hello world\"\n}"); 231 } 232 233 SECTION("indent=x") 234 { 235 CHECK(j.dump().size() == 94); 236 CHECK(j.dump(1).size() == 127); 237 CHECK(j.dump(2).size() == 142); 238 CHECK(j.dump(512).size() == 7792); 239 240 // important test, because it yields a resize of the indent_string 241 // inside the dump() function 242 CHECK(j.dump(1024).size() == 15472); 243 244 const auto binary = json::binary({1, 2, 3}, 128); 245 CHECK(binary.dump(1024).size() == 2086); 246 } 247 248 SECTION("dump and floating-point numbers") 249 { 250 auto s = json(42.23).dump(); 251 CHECK(s.find("42.23") != std::string::npos); 252 } 253 254 SECTION("dump and small floating-point numbers") 255 { 256 auto s = json(1.23456e-78).dump(); 257 CHECK(s.find("1.23456e-78") != std::string::npos); 258 } 259 260 SECTION("dump and non-ASCII characters") 261 { 262 CHECK(json("ä").dump() == "\"ä\""); 263 CHECK(json("Ö").dump() == "\"Ö\""); 264 CHECK(json("❤️").dump() == "\"❤️\""); 265 } 266 267 SECTION("dump with ensure_ascii and non-ASCII characters") 268 { 269 CHECK(json("ä").dump(-1, ' ', true) == "\"\\u00e4\""); 270 CHECK(json("Ö").dump(-1, ' ', true) == "\"\\u00d6\""); 271 CHECK(json("❤️").dump(-1, ' ', true) == "\"\\u2764\\ufe0f\""); 272 } 273 274 SECTION("full Unicode escaping to ASCII") 275 { 276 SECTION("parsing yields the same JSON value") 277 { 278 std::ifstream f_escaped(TEST_DATA_DIRECTORY "/json_nlohmann_tests/all_unicode_ascii.json"); 279 std::ifstream f_unescaped(TEST_DATA_DIRECTORY "/json_nlohmann_tests/all_unicode.json"); 280 281 json j1 = json::parse(f_escaped); 282 json j2 = json::parse(f_unescaped); 283 CHECK(j1 == j2); 284 } 285 286 SECTION("dumping yields the same JSON text") 287 { 288 std::ifstream f_escaped(TEST_DATA_DIRECTORY "/json_nlohmann_tests/all_unicode_ascii.json"); 289 std::ifstream f_unescaped(TEST_DATA_DIRECTORY "/json_nlohmann_tests/all_unicode.json"); 290 291 json value = json::parse(f_unescaped); 292 std::string text = value.dump(4, ' ', true); 293 294 std::string expected((std::istreambuf_iterator<char>(f_escaped)), 295 std::istreambuf_iterator<char>()); 296 CHECK(text == expected); 297 } 298 } 299 300 SECTION("serialization of discarded element") 301 { 302 json j_discarded(json::value_t::discarded); 303 CHECK(j_discarded.dump() == "<discarded>"); 304 } 305 306 SECTION("check that precision is reset after serialization") 307 { 308 // create stringstream and set precision 309 std::stringstream ss; 310 ss.precision(3); 311 ss << 3.141592653589793 << std::fixed; 312 CHECK(ss.str() == "3.14"); 313 314 // reset stringstream 315 ss.str(std::string()); 316 317 // use stringstream for JSON serialization 318 json j_number = 3.14159265358979; 319 ss << j_number; 320 321 // check that precision has been overridden during serialization 322 CHECK(ss.str() == "3.14159265358979"); 323 324 // check that precision has been restored 325 CHECK(ss.precision() == 3); 326 } 327 } 328 329 SECTION("round trips") 330 { 331 for (const auto& s : 332 {"3.141592653589793", "1000000000000000010E5" 333 }) 334 { 335 json j1 = json::parse(s); 336 std::string s1 = j1.dump(); 337 json j2 = json::parse(s1); 338 std::string s2 = j2.dump(); 339 CHECK(s1 == s2); 340 } 341 } 342 343 SECTION("return the type of the object (explicit)") 344 { 345 SECTION("null") 346 { 347 json j = nullptr; 348 CHECK(j.type() == json::value_t::null); 349 } 350 351 SECTION("object") 352 { 353 json j = {{"foo", "bar"}}; 354 CHECK(j.type() == json::value_t::object); 355 } 356 357 SECTION("array") 358 { 359 json j = {1, 2, 3, 4}; 360 CHECK(j.type() == json::value_t::array); 361 } 362 363 SECTION("boolean") 364 { 365 json j = true; 366 CHECK(j.type() == json::value_t::boolean); 367 } 368 369 SECTION("string") 370 { 371 json j = "Hello world"; 372 CHECK(j.type() == json::value_t::string); 373 } 374 375 SECTION("number (integer)") 376 { 377 json j = 23; 378 CHECK(j.type() == json::value_t::number_integer); 379 } 380 381 SECTION("number (unsigned)") 382 { 383 json j = 23u; 384 CHECK(j.type() == json::value_t::number_unsigned); 385 } 386 387 SECTION("number (floating-point)") 388 { 389 json j = 42.23; 390 CHECK(j.type() == json::value_t::number_float); 391 } 392 } 393 394 SECTION("return the type of the object (implicit)") 395 { 396 SECTION("null") 397 { 398 json j = nullptr; 399 json::value_t t = j; 400 CHECK(t == j.type()); 401 } 402 403 SECTION("object") 404 { 405 json j = {{"foo", "bar"}}; 406 json::value_t t = j; 407 CHECK(t == j.type()); 408 } 409 410 SECTION("array") 411 { 412 json j = {1, 2, 3, 4}; 413 json::value_t t = j; 414 CHECK(t == j.type()); 415 } 416 417 SECTION("boolean") 418 { 419 json j = true; 420 json::value_t t = j; 421 CHECK(t == j.type()); 422 } 423 424 SECTION("string") 425 { 426 json j = "Hello world"; 427 json::value_t t = j; 428 CHECK(t == j.type()); 429 } 430 431 SECTION("number (integer)") 432 { 433 json j = 23; 434 json::value_t t = j; 435 CHECK(t == j.type()); 436 } 437 438 SECTION("number (unsigned)") 439 { 440 json j = 23u; 441 json::value_t t = j; 442 CHECK(t == j.type()); 443 } 444 445 SECTION("number (floating-point)") 446 { 447 json j = 42.23; 448 json::value_t t = j; 449 CHECK(t == j.type()); 450 } 451 452 SECTION("binary") 453 { 454 json j = json::binary({}); 455 json::value_t t = j; 456 CHECK(t == j.type()); 457 } 458 } 459 } 460