• 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 // Copyright (c) 2013-2019 Niels Lohmann <http://nlohmann.me>.
7 // SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
8 // SPDX-License-Identifier: MIT
9 
10 #include <set>
11 #include <sstream>
12 #include <string>
13 
14 #include "doctest_compatibility.h"
15 
16 #include <nlohmann/json.hpp>
17 
18 // Test extending nlohmann::json by using a custom base class.
19 // Add some metadata to each node and test the behaviour of copy / move
20 template<class MetaDataType>
21 class json_metadata
22 {
23   public:
24     using metadata_t = MetaDataType;
metadata()25     metadata_t& metadata()
26     {
27         return m_metadata;
28     }
metadata() const29     const metadata_t& metadata() const
30     {
31         return m_metadata;
32     }
33   private:
34     metadata_t m_metadata = {};
35 };
36 
37 template<class T>
38 using json_with_metadata =
39     nlohmann::basic_json <
40     std::map,
41     std::vector,
42     std::string,
43     bool,
44     std::int64_t,
45     std::uint64_t,
46     double,
47     std::allocator,
48     nlohmann::adl_serializer,
49     std::vector<std::uint8_t>,
50     json_metadata<T>
51     >;
52 
53 TEST_CASE("JSON Node Metadata")
54 {
55     SECTION("type int")
56     {
57         using json = json_with_metadata<int>;
58         json null;
59         auto obj   = json::object();
60         auto array = json::array();
61 
62         null.metadata()  = 1;
63         obj.metadata()   = 2;
64         array.metadata() = 3;
65         auto copy = array;
66 
67         CHECK(null.metadata()  == 1);
68         CHECK(obj.metadata()   == 2);
69         CHECK(array.metadata() == 3);
70         CHECK(copy.metadata()  == 3);
71     }
72     SECTION("type vector<int>")
73     {
74         using json = json_with_metadata<std::vector<int>>;
75         json value;
76         value.metadata().emplace_back(1);
77         auto copy = value;
78         value.metadata().emplace_back(2);
79 
80         CHECK(copy.metadata().size()  == 1);
81         CHECK(copy.metadata().at(0)   == 1);
82         CHECK(value.metadata().size() == 2);
83         CHECK(value.metadata().at(0)  == 1);
84         CHECK(value.metadata().at(1)  == 2);
85     }
86     SECTION("copy ctor")
87     {
88         using json = json_with_metadata<std::vector<int>>;
89         json value;
90         value.metadata().emplace_back(1);
91         value.metadata().emplace_back(2);
92 
93         json copy = value;
94 
95         CHECK(copy.metadata().size()  == 2);
96         CHECK(copy.metadata().at(0)   == 1);
97         CHECK(copy.metadata().at(1)   == 2);
98         CHECK(value.metadata().size() == 2);
99         CHECK(value.metadata().at(0)  == 1);
100         CHECK(value.metadata().at(1)  == 2);
101 
102         value.metadata().clear();
103         CHECK(copy.metadata().size()  == 2);
104         CHECK(value.metadata().size() == 0);
105     }
106     SECTION("move ctor")
107     {
108         using json = json_with_metadata<std::vector<int>>;
109         json value;
110         value.metadata().emplace_back(1);
111         value.metadata().emplace_back(2);
112 
113         const json moved = std::move(value);
114 
115         CHECK(moved.metadata().size()  == 2);
116         CHECK(moved.metadata().at(0)   == 1);
117         CHECK(moved.metadata().at(1)   == 2);
118     }
119     SECTION("move assign")
120     {
121         using json = json_with_metadata<std::vector<int>>;
122         json value;
123         value.metadata().emplace_back(1);
124         value.metadata().emplace_back(2);
125 
126         json moved;
127         moved = std::move(value);
128 
129         CHECK(moved.metadata().size()  == 2);
130         CHECK(moved.metadata().at(0)   == 1);
131         CHECK(moved.metadata().at(1)   == 2);
132     }
133     SECTION("copy assign")
134     {
135         using json = json_with_metadata<std::vector<int>>;
136         json value;
137         value.metadata().emplace_back(1);
138         value.metadata().emplace_back(2);
139 
140         json copy;
141         copy = value;
142 
143         CHECK(copy.metadata().size()  == 2);
144         CHECK(copy.metadata().at(0)   == 1);
145         CHECK(copy.metadata().at(1)   == 2);
146         CHECK(value.metadata().size() == 2);
147         CHECK(value.metadata().at(0)  == 1);
148         CHECK(value.metadata().at(1)  == 2);
149 
150         value.metadata().clear();
151         CHECK(copy.metadata().size()  == 2);
152         CHECK(value.metadata().size() == 0);
153     }
154     SECTION("type unique_ptr<int>")
155     {
156         using json = json_with_metadata<std::unique_ptr<int>>;
157         json value;
158         value.metadata().reset(new int(42)); // NOLINT(cppcoreguidelines-owning-memory)
159         auto moved = std::move(value);
160 
161         CHECK(moved.metadata() != nullptr);
162         CHECK(*moved.metadata() == 42);
163     }
164     SECTION("type vector<int> in json array")
165     {
166         using json = json_with_metadata<std::vector<int>>;
167         json value;
168         value.metadata().emplace_back(1);
169         value.metadata().emplace_back(2);
170 
171         json const array(10, value);
172 
173         CHECK(value.metadata().size() == 2);
174         CHECK(value.metadata().at(0)  == 1);
175         CHECK(value.metadata().at(1)  == 2);
176 
177         for (const auto& val : array)
178         {
179             CHECK(val.metadata().size() == 2);
180             CHECK(val.metadata().at(0)  == 1);
181             CHECK(val.metadata().at(1)  == 2);
182         }
183     }
184 }
185 
186 // Test extending nlohmann::json by using a custom base class.
187 // Add a custom member function template iterating over the whole json tree.
188 class visitor_adaptor
189 {
190   public:
191     template <class Fnc>
192     void visit(const Fnc& fnc) const;
193   private:
194     template <class Ptr, class Fnc>
195     void do_visit(const Ptr& ptr, const Fnc& fnc) const;
196 };
197 
198 using json_with_visitor_t = nlohmann::basic_json <
199                             std::map,
200                             std::vector,
201                             std::string,
202                             bool,
203                             std::int64_t,
204                             std::uint64_t,
205                             double,
206                             std::allocator,
207                             nlohmann::adl_serializer,
208                             std::vector<std::uint8_t>,
209                             visitor_adaptor
210                             >;
211 
212 template <class Fnc>
visit(const Fnc & fnc) const213 void visitor_adaptor::visit(const Fnc& fnc) const
214 {
215     do_visit(json_with_visitor_t::json_pointer{}, fnc);
216 }
217 
218 template <class Ptr, class Fnc>
do_visit(const Ptr & ptr,const Fnc & fnc) const219 void visitor_adaptor::do_visit(const Ptr& ptr, const Fnc& fnc) const
220 {
221     using value_t = nlohmann::detail::value_t;
222     const json_with_visitor_t& json = *static_cast<const json_with_visitor_t*>(this); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
223     switch (json.type())
224     {
225         case value_t::object:
226             for (const auto& entry : json.items())
227             {
228                 entry.value().do_visit(ptr / entry.key(), fnc);
229             }
230             break;
231         case value_t::array:
232             for (std::size_t i = 0; i < json.size(); ++i)
233             {
234                 json.at(i).do_visit(ptr / std::to_string(i), fnc);
235             }
236             break;
237         case value_t::discarded:
238             break;
239         case value_t::null:
240         case value_t::string:
241         case value_t::boolean:
242         case value_t::number_integer:
243         case value_t::number_unsigned:
244         case value_t::number_float:
245         case value_t::binary:
246         default:
247             fnc(ptr, json);
248     }
249 }
250 
251 TEST_CASE("JSON Visit Node")
252 {
253     json_with_visitor_t json;
254     json["null"];
255     json["int"]  = -1;
256     json["uint"] = 1U;
257     json["float"] = 1.0;
258     json["boolean"] = true;
259     json["string"] = "string";
260     json["array"].push_back(0);
261     json["array"].push_back(1);
262     json["array"].push_back(json);
263 
264     std::set<std::string> expected
265     {
266         "/null - null - null",
267         "/int - number_integer - -1",
268         "/uint - number_unsigned - 1",
269         "/float - number_float - 1.0",
270         "/boolean - boolean - true",
271         "/string - string - \"string\"",
272         "/array/0 - number_integer - 0",
273         "/array/1 - number_integer - 1",
274 
275         "/array/2/null - null - null",
276         "/array/2/int - number_integer - -1",
277         "/array/2/uint - number_unsigned - 1",
278         "/array/2/float - number_float - 1.0",
279         "/array/2/boolean - boolean - true",
280         "/array/2/string - string - \"string\"",
281         "/array/2/array/0 - number_integer - 0",
282         "/array/2/array/1 - number_integer - 1"
283     };
284 
285     json.visit(
286         [&](const json_with_visitor_t::json_pointer & p,
287             const json_with_visitor_t& j)
__anonf2bb344a0102(const json_with_visitor_t::json_pointer & p, const json_with_visitor_t& j) 288     {
289         std::stringstream str;
290         str << p.to_string() << " - " ;
291         using value_t = nlohmann::detail::value_t;
292         switch (j.type())
293         {
294             case value_t::object:
295                 str << "object";
296                 break;
297             case value_t::array:
298                 str << "array";
299                 break;
300             case value_t::discarded:
301                 str << "discarded";
302                 break;
303             case value_t::null:
304                 str << "null";
305                 break;
306             case value_t::string:
307                 str << "string";
308                 break;
309             case value_t::boolean:
310                 str << "boolean";
311                 break;
312             case value_t::number_integer:
313                 str << "number_integer";
314                 break;
315             case value_t::number_unsigned:
316                 str << "number_unsigned";
317                 break;
318             case value_t::number_float:
319                 str << "number_float";
320                 break;
321             case value_t::binary:
322                 str << "binary";
323                 break;
324             default:
325                 str << "error";
326                 break;
327         }
328         str << " - "  << j.dump();
329         CHECK(json.at(p) == j);
330         INFO(str.str());
331         CHECK(expected.count(str.str()) == 1);
332         expected.erase(str.str());
333     }
334     );
335     CHECK(expected.empty());
336 }
337