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 <string>
10#include <vector>
11#include "doctest_compatibility.h"
12
13#include <nlohmann/json.hpp>
14using nlohmann::json;
15
16namespace persons
17{
18class person_with_private_data
19{
20  private:
21    std::string name{};
22    int age = 0;
23    json metadata = nullptr;
24
25  public:
26    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;
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
41class person_with_private_data_2
42{
43  private:
44    std::string name{};
45    int age = 0;
46    json metadata = nullptr;
47
48  public:
49    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;
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
61    std::string getName() const
62    {
63        return name;
64    }
65    int getAge() const
66    {
67        return age;
68    }
69    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
77class person_without_private_data_1
78{
79  public:
80    std::string name{};
81    int age = 0;
82    json metadata = nullptr;
83
84    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;
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
99class person_without_private_data_2
100{
101  public:
102    std::string name{};
103    int age = 0;
104    json metadata = nullptr;
105
106    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;
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
119NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person_without_private_data_2, age, name, metadata)
120
121class person_without_private_data_3
122{
123  public:
124    std::string name{};
125    int age = 0;
126    json metadata = nullptr;
127
128    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;
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
140    std::string getName() const
141    {
142        return name;
143    }
144    int getAge() const
145    {
146        return age;
147    }
148    json getMetadata() const
149    {
150        return metadata;
151    }
152};
153
154NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(person_without_private_data_3, age, name, metadata)
155
156class person_with_private_alphabet
157{
158  public:
159    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
219class person_with_public_alphabet
220{
221  public:
222    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
280NLOHMANN_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} // namespace persons
283
284TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRUSIVE and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE", T,
285                   persons::person_with_private_data,
286                   persons::person_without_private_data_1,
287                   persons::person_without_private_data_2)
288{
289    SECTION("person")
290    {
291        // serialization
292        T p1("Erik", 1, {{"haircuts", 2}});
293        CHECK(json(p1).dump() == "{\"age\":1,\"metadata\":{\"haircuts\":2},\"name\":\"Erik\"}");
294
295        // deserialization
296        auto p2 = json(p1).get<T>();
297        CHECK(p2 == p1);
298
299        // roundtrip
300        CHECK(T(json(p1)) == p1);
301        CHECK(json(T(json(p1))) == json(p1));
302
303        // check exception in case of missing field
304        json j = json(p1);
305        j.erase("age");
306        CHECK_THROWS_WITH_AS(j.get<T>(), "[json.exception.out_of_range.403] key 'age' not found", json::out_of_range);
307    }
308}
309
310TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT", T,
311                   persons::person_with_private_data_2,
312                   persons::person_without_private_data_3)
313{
314    SECTION("person with default values")
315    {
316        // serialization of default constructed object
317        T p0;
318        CHECK(json(p0).dump() == "{\"age\":0,\"metadata\":null,\"name\":\"\"}");
319
320        // serialization
321        T p1("Erik", 1, {{"haircuts", 2}});
322        CHECK(json(p1).dump() == "{\"age\":1,\"metadata\":{\"haircuts\":2},\"name\":\"Erik\"}");
323
324        // deserialization
325        auto p2 = json(p1).get<T>();
326        CHECK(p2 == p1);
327
328        // roundtrip
329        CHECK(T(json(p1)) == p1);
330        CHECK(json(T(json(p1))) == json(p1));
331
332        // check default value in case of missing field
333        json j = json(p1);
334        j.erase("name");
335        j.erase("age");
336        j.erase("metadata");
337        T p3 = j.get<T>();
338        CHECK(p3.getName() == "");
339        CHECK(p3.getAge() == 0);
340        CHECK(p3.getMetadata() == nullptr);
341    }
342}
343
344TEST_CASE_TEMPLATE("Serialization/deserialization of classes with 26 public/private member variables via NLOHMANN_DEFINE_TYPE_INTRUSIVE and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE", T,
345                   persons::person_with_private_alphabet,
346                   persons::person_with_public_alphabet)
347{
348    SECTION("alphabet")
349    {
350        {
351            T obj1;
352            nlohmann::json j = obj1; //via json object
353            T obj2;
354            j.get_to(obj2);
355            bool ok = (obj1 == obj2);
356            CHECK(ok);
357        }
358
359        {
360            T obj1;
361            nlohmann::json j1 = obj1; //via json string
362            std::string s = j1.dump();
363            nlohmann::json j2 = nlohmann::json::parse(s);
364            T obj2;
365            j2.get_to(obj2);
366            bool ok = (obj1 == obj2);
367            CHECK(ok);
368        }
369
370        {
371            T obj1;
372            nlohmann::json j1 = obj1; //via msgpack
373            std::vector<uint8_t> buf = nlohmann::json::to_msgpack(j1);
374            nlohmann::json j2 = nlohmann::json::from_msgpack(buf);
375            T obj2;
376            j2.get_to(obj2);
377            bool ok = (obj1 == obj2);
378            CHECK(ok);
379        }
380
381        {
382            T obj1;
383            nlohmann::json j1 = obj1; //via bson
384            std::vector<uint8_t> buf = nlohmann::json::to_bson(j1);
385            nlohmann::json j2 = nlohmann::json::from_bson(buf);
386            T obj2;
387            j2.get_to(obj2);
388            bool ok = (obj1 == obj2);
389            CHECK(ok);
390        }
391
392        {
393            T obj1;
394            nlohmann::json j1 = obj1; //via cbor
395            std::vector<uint8_t> buf = nlohmann::json::to_cbor(j1);
396            nlohmann::json j2 = nlohmann::json::from_cbor(buf);
397            T obj2;
398            j2.get_to(obj2);
399            bool ok = (obj1 == obj2);
400            CHECK(ok);
401        }
402
403        {
404            T obj1;
405            nlohmann::json j1 = obj1; //via ubjson
406            std::vector<uint8_t> buf = nlohmann::json::to_ubjson(j1);
407            nlohmann::json j2 = nlohmann::json::from_ubjson(buf);
408            T obj2;
409            j2.get_to(obj2);
410            bool ok = (obj1 == obj2);
411            CHECK(ok);
412        }
413    }
414}
415