1 // __ _____ _____ _____ 2 // __| | __| | | | JSON for Modern C++ (supporting code) 3 // | | |__ | | | | | | version 3.11.3 4 // |_____|_____|_____|_|___| https://github.com/nlohmann/json 5 // 6 // SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me> 7 // SPDX-License-Identifier: MIT 8 9 #include <string> 10 #include <vector> 11 #include "doctest_compatibility.h" 12 13 #include <nlohmann/json.hpp> 14 using nlohmann::json; 15 16 namespace persons 17 { 18 class person_with_private_data 19 { 20 private: 21 std::string name{}; 22 int age = 0; 23 json metadata = nullptr; 24 25 public: operator ==(const person_with_private_data & rhs) const26 bool operator==(const person_with_private_data& rhs) const 27 { 28 return name == rhs.name && age == rhs.age && metadata == rhs.metadata; 29 } 30 31 person_with_private_data() = default; person_with_private_data(std::string name_,int age_,json metadata_)32 person_with_private_data(std::string name_, int age_, json metadata_) 33 : name(std::move(name_)) 34 , age(age_) 35 , metadata(std::move(metadata_)) 36 {} 37 38 NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_with_private_data, age, name, metadata) 39 }; 40 41 class person_with_private_data_2 42 { 43 private: 44 std::string name{}; 45 int age = 0; 46 json metadata = nullptr; 47 48 public: operator ==(const person_with_private_data_2 & rhs) const49 bool operator==(const person_with_private_data_2& rhs) const 50 { 51 return name == rhs.name && age == rhs.age && metadata == rhs.metadata; 52 } 53 54 person_with_private_data_2() = default; person_with_private_data_2(std::string name_,int age_,json metadata_)55 person_with_private_data_2(std::string name_, int age_, json metadata_) 56 : name(std::move(name_)) 57 , age(age_) 58 , metadata(std::move(metadata_)) 59 {} 60 getName() const61 std::string getName() const 62 { 63 return name; 64 } getAge() const65 int getAge() const 66 { 67 return age; 68 } getMetadata() const69 json getMetadata() const 70 { 71 return metadata; 72 } 73 74 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(person_with_private_data_2, age, name, metadata) 75 }; 76 77 class person_without_private_data_1 78 { 79 public: 80 std::string name{}; 81 int age = 0; 82 json metadata = nullptr; 83 operator ==(const person_without_private_data_1 & rhs) const84 bool operator==(const person_without_private_data_1& rhs) const 85 { 86 return name == rhs.name && age == rhs.age && metadata == rhs.metadata; 87 } 88 89 person_without_private_data_1() = default; person_without_private_data_1(std::string name_,int age_,json metadata_)90 person_without_private_data_1(std::string name_, int age_, json metadata_) 91 : name(std::move(name_)) 92 , age(age_) 93 , metadata(std::move(metadata_)) 94 {} 95 96 NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_without_private_data_1, age, name, metadata) 97 }; 98 99 class person_without_private_data_2 100 { 101 public: 102 std::string name{}; 103 int age = 0; 104 json metadata = nullptr; 105 operator ==(const person_without_private_data_2 & rhs) const106 bool operator==(const person_without_private_data_2& rhs) const 107 { 108 return name == rhs.name && age == rhs.age && metadata == rhs.metadata; 109 } 110 111 person_without_private_data_2() = default; person_without_private_data_2(std::string name_,int age_,json metadata_)112 person_without_private_data_2(std::string name_, int age_, json metadata_) 113 : name(std::move(name_)) 114 , age(age_) 115 , metadata(std::move(metadata_)) 116 {} 117 }; 118 119 NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person_without_private_data_2, age, name, metadata) 120 121 class person_without_private_data_3 122 { 123 public: 124 std::string name{}; 125 int age = 0; 126 json metadata = nullptr; 127 operator ==(const person_without_private_data_3 & rhs) const128 bool operator==(const person_without_private_data_3& rhs) const 129 { 130 return name == rhs.name && age == rhs.age && metadata == rhs.metadata; 131 } 132 133 person_without_private_data_3() = default; person_without_private_data_3(std::string name_,int age_,json metadata_)134 person_without_private_data_3(std::string name_, int age_, json metadata_) 135 : name(std::move(name_)) 136 , age(age_) 137 , metadata(std::move(metadata_)) 138 {} 139 getName() const140 std::string getName() const 141 { 142 return name; 143 } getAge() const144 int getAge() const 145 { 146 return age; 147 } getMetadata() const148 json getMetadata() const 149 { 150 return metadata; 151 } 152 }; 153 154 NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(person_without_private_data_3, age, name, metadata) 155 156 class person_with_private_alphabet 157 { 158 public: operator ==(const person_with_private_alphabet & other) const159 bool operator==(const person_with_private_alphabet& other) const 160 { 161 return a == other.a && 162 b == other.b && 163 c == other.c && 164 d == other.d && 165 e == other.e && 166 f == other.f && 167 g == other.g && 168 h == other.h && 169 i == other.i && 170 j == other.j && 171 k == other.k && 172 l == other.l && 173 m == other.m && 174 n == other.n && 175 o == other.o && 176 p == other.p && 177 q == other.q && 178 r == other.r && 179 s == other.s && 180 t == other.t && 181 u == other.u && 182 v == other.v && 183 w == other.w && 184 x == other.x && 185 y == other.y && 186 z == other.z; 187 } 188 189 private: 190 int a = 0; 191 int b = 0; 192 int c = 0; 193 int d = 0; 194 int e = 0; 195 int f = 0; 196 int g = 0; 197 int h = 0; 198 int i = 0; 199 int j = 0; 200 int k = 0; 201 int l = 0; 202 int m = 0; 203 int n = 0; 204 int o = 0; 205 int p = 0; 206 int q = 0; 207 int r = 0; 208 int s = 0; 209 int t = 0; 210 int u = 0; 211 int v = 0; 212 int w = 0; 213 int x = 0; 214 int y = 0; 215 int z = 0; 216 NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_with_private_alphabet, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z) 217 }; 218 219 class person_with_public_alphabet 220 { 221 public: operator ==(const person_with_public_alphabet & other) const222 bool operator==(const person_with_public_alphabet& other) const 223 { 224 return a == other.a && 225 b == other.b && 226 c == other.c && 227 d == other.d && 228 e == other.e && 229 f == other.f && 230 g == other.g && 231 h == other.h && 232 i == other.i && 233 j == other.j && 234 k == other.k && 235 l == other.l && 236 m == other.m && 237 n == other.n && 238 o == other.o && 239 p == other.p && 240 q == other.q && 241 r == other.r && 242 s == other.s && 243 t == other.t && 244 u == other.u && 245 v == other.v && 246 w == other.w && 247 x == other.x && 248 y == other.y && 249 z == other.z; 250 } 251 252 int a = 0; 253 int b = 0; 254 int c = 0; 255 int d = 0; 256 int e = 0; 257 int f = 0; 258 int g = 0; 259 int h = 0; 260 int i = 0; 261 int j = 0; 262 int k = 0; 263 int l = 0; 264 int m = 0; 265 int n = 0; 266 int o = 0; 267 int p = 0; 268 int q = 0; 269 int r = 0; 270 int s = 0; 271 int t = 0; 272 int u = 0; 273 int v = 0; 274 int w = 0; 275 int x = 0; 276 int y = 0; 277 int z = 0; 278 }; 279 280 NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person_with_public_alphabet, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z) 281 282 class person_without_default_constructor_1 283 { 284 public: 285 std::string name; 286 int age; 287 operator ==(const person_without_default_constructor_1 & other) const288 bool operator==(const person_without_default_constructor_1& other) const 289 { 290 return name == other.name && age == other.age; 291 } 292 person_without_default_constructor_1(std::string name_,int age_)293 person_without_default_constructor_1(std::string name_, int age_) 294 : name{std::move(name_)} 295 , age{age_} 296 {} 297 298 NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(person_without_default_constructor_1, name, age) 299 }; 300 301 class person_without_default_constructor_2 302 { 303 public: 304 std::string name; 305 int age; 306 operator ==(const person_without_default_constructor_2 & other) const307 bool operator==(const person_without_default_constructor_2& other) const 308 { 309 return name == other.name && age == other.age; 310 } 311 person_without_default_constructor_2(std::string name_,int age_)312 person_without_default_constructor_2(std::string name_, int age_) 313 : name{std::move(name_)} 314 , age{age_} 315 {} 316 }; 317 318 NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(person_without_default_constructor_2, name, age) 319 320 } // namespace persons 321 322 TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRUSIVE and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE", T, 323 persons::person_with_private_data, 324 persons::person_without_private_data_1, 325 persons::person_without_private_data_2) 326 { 327 SECTION("person") 328 { 329 // serialization 330 T p1("Erik", 1, {{"haircuts", 2}}); 331 CHECK(json(p1).dump() == "{\"age\":1,\"metadata\":{\"haircuts\":2},\"name\":\"Erik\"}"); 332 333 // deserialization 334 auto p2 = json(p1).get<T>(); 335 CHECK(p2 == p1); 336 337 // roundtrip 338 CHECK(T(json(p1)) == p1); 339 CHECK(json(T(json(p1))) == json(p1)); 340 341 // check exception in case of missing field 342 json j = json(p1); 343 j.erase("age"); 344 CHECK_THROWS_WITH_AS(j.get<T>(), "[json.exception.out_of_range.403] key 'age' not found", json::out_of_range); 345 } 346 } 347 348 TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT", T, 349 persons::person_with_private_data_2, 350 persons::person_without_private_data_3) 351 { 352 SECTION("person with default values") 353 { 354 // serialization of default constructed object 355 T p0; 356 CHECK(json(p0).dump() == "{\"age\":0,\"metadata\":null,\"name\":\"\"}"); 357 358 // serialization 359 T p1("Erik", 1, {{"haircuts", 2}}); 360 CHECK(json(p1).dump() == "{\"age\":1,\"metadata\":{\"haircuts\":2},\"name\":\"Erik\"}"); 361 362 // deserialization 363 auto p2 = json(p1).get<T>(); 364 CHECK(p2 == p1); 365 366 // roundtrip 367 CHECK(T(json(p1)) == p1); 368 CHECK(json(T(json(p1))) == json(p1)); 369 370 // check default value in case of missing field 371 json j = json(p1); 372 j.erase("name"); 373 j.erase("age"); 374 j.erase("metadata"); 375 T p3 = j.get<T>(); 376 CHECK(p3.getName() == ""); 377 CHECK(p3.getAge() == 0); 378 CHECK(p3.getMetadata() == nullptr); 379 } 380 } 381 382 TEST_CASE_TEMPLATE("Serialization/deserialization of classes with 26 public/private member variables via NLOHMANN_DEFINE_TYPE_INTRUSIVE and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE", T, 383 persons::person_with_private_alphabet, 384 persons::person_with_public_alphabet) 385 { 386 SECTION("alphabet") 387 { 388 { 389 T obj1; 390 nlohmann::json const j = obj1; //via json object 391 T obj2; 392 j.get_to(obj2); 393 bool ok = (obj1 == obj2); 394 CHECK(ok); 395 } 396 397 { 398 T obj1; 399 nlohmann::json const j1 = obj1; //via json string 400 std::string const s = j1.dump(); 401 nlohmann::json const j2 = nlohmann::json::parse(s); 402 T obj2; 403 j2.get_to(obj2); 404 bool ok = (obj1 == obj2); 405 CHECK(ok); 406 } 407 408 { 409 T obj1; 410 nlohmann::json const j1 = obj1; //via msgpack 411 std::vector<uint8_t> const buf = nlohmann::json::to_msgpack(j1); 412 nlohmann::json const j2 = nlohmann::json::from_msgpack(buf); 413 T obj2; 414 j2.get_to(obj2); 415 bool ok = (obj1 == obj2); 416 CHECK(ok); 417 } 418 419 { 420 T obj1; 421 nlohmann::json const j1 = obj1; //via bson 422 std::vector<uint8_t> const buf = nlohmann::json::to_bson(j1); 423 nlohmann::json const j2 = nlohmann::json::from_bson(buf); 424 T obj2; 425 j2.get_to(obj2); 426 bool ok = (obj1 == obj2); 427 CHECK(ok); 428 } 429 430 { 431 T obj1; 432 nlohmann::json const j1 = obj1; //via cbor 433 std::vector<uint8_t> const buf = nlohmann::json::to_cbor(j1); 434 nlohmann::json const j2 = nlohmann::json::from_cbor(buf); 435 T obj2; 436 j2.get_to(obj2); 437 bool ok = (obj1 == obj2); 438 CHECK(ok); 439 } 440 441 { 442 T obj1; 443 nlohmann::json const j1 = obj1; //via ubjson 444 std::vector<uint8_t> const buf = nlohmann::json::to_ubjson(j1); 445 nlohmann::json const j2 = nlohmann::json::from_ubjson(buf); 446 T obj2; 447 j2.get_to(obj2); 448 bool ok = (obj1 == obj2); 449 CHECK(ok); 450 } 451 } 452 } 453 454 TEST_CASE_TEMPLATE("Serialization of non-default-constructible classes via NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE", T, 455 persons::person_without_default_constructor_1, 456 persons::person_without_default_constructor_2) 457 { 458 SECTION("person") 459 { 460 { 461 // serialization of a single object 462 T person{"Erik", 1}; 463 CHECK(json(person).dump() == "{\"age\":1,\"name\":\"Erik\"}"); 464 465 // serialization of a container with objects 466 std::vector<T> const two_persons 467 { 468 {"Erik", 1}, 469 {"Kyle", 2} 470 }; 471 CHECK(json(two_persons).dump() == "[{\"age\":1,\"name\":\"Erik\"},{\"age\":2,\"name\":\"Kyle\"}]"); 472 } 473 } 474 } 475