• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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