Botcraft 1.21.4
Loading...
Searching...
No Matches
Json.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cassert>
4#include <map>
5#include <stdexcept>
6#include <string>
7#include <string_view>
8#include <type_traits>
9#include <variant>
10#include <vector>
11#include <array>
12
14
15namespace ProtocolCraft
16{
17 class NetworkType;
18
19 namespace Json
20 {
21 // Forward declaration
22 class Array;
23 class Object;
24
25 namespace Internal
26 {
27 /// @brief std::variant holding the actual data
28 using JsonVariant = std::variant<
29 std::monostate,
32 std::string,
33 bool,
34 long long int,
35 unsigned long long int,
36 double
37 >;
38 }
39
40
41 /// @brief Main class, basically a JsonVariant with extra utility functions
42 /// it doesn't inherit JsonVariant directly so we can better control which
43 /// constructors are allowed
44 class Value
45 {
46 public:
47 Value(std::nullptr_t = nullptr);
48 Value(std::string_view s);
49 Value(const std::string& s);
50 Value(std::string&& s);
51 Value(const char* s);
52 Value(const bool b);
53 Value(const Object& o);
54 Value(Object&& o);
55 Value(const Array& a);
56 Value(Array&& a);
57 Value(const std::initializer_list<Value>& init);
58 Value(const NetworkType& o);
59
60 // Add support for any std::vector<T>, std::deque<T>, std::list<T> etc... when T is compatible with Value
61 template<
62 template<typename, typename> class C,
63 typename T,
64 typename A = std::allocator<T>,
65 std::enable_if_t<std::is_convertible_v<T, Value>, bool> = true
66 >
67 Value(const C<T, A>& c);
68
69 // Add support for any std::array<T, N> when T is compatible with Value
70 template<
71 typename T,
72 size_t N,
73 std::enable_if_t<std::is_convertible_v<T, Value>, bool> = true
74 >
75 Value(const std::array<T, N>& v);
76
77 // Add support for any std::map<std::string, T> when T is compatible with Value
78 template<
79 typename T,
80 std::enable_if_t<std::is_convertible_v<T, Value>, bool> = true
81 >
82 Value(const std::map<std::string, T>& m);
83
84 // Add support for all (unsigned) int/char/short
85 template<
86 typename T,
87 std::enable_if_t<std::is_integral_v<T>&& std::is_unsigned_v<T>, bool> = true
88 >
89 Value(const T u);
90
91 template<
92 typename T,
93 std::enable_if_t<(std::is_integral_v<T>&& std::is_signed_v<T>) || std::is_enum_v<T>, bool> = true
94 >
95 Value(const T i);
96
97 template<
98 typename T,
99 std::enable_if_t<std::is_floating_point_v<T>, bool> = true
100 >
101 Value(const T f);
102
103 template<
104 typename T,
105 std::enable_if_t<!std::is_same_v<T, std::monostate> && !std::is_same_v<T, Object> && !std::is_same_v<T, Array> && !std::is_same_v<T, std::string>, bool> = true
106 >
107 T get() const;
108
109 template<
110 typename T = double,
111 std::enable_if_t<std::is_integral_v<T> || std::is_floating_point_v<T>, bool> = true
112 >
113 T get_number() const;
114
115 template<
116 typename T,
117 std::enable_if_t<std::is_same_v<T, Object> || std::is_same_v<T, Array> || std::is_same_v<T, std::string>, bool> = true
118 >
119 T& get();
120
121 template<
122 typename T,
123 std::enable_if_t<std::is_same_v<T, Object> || std::is_same_v<T, Array> || std::is_same_v<T, std::string>, bool> = true
124 >
125 const T& get() const;
126
128 Array& get_array();
129 std::string& get_string();
130
131 const Object& get_object() const;
132 const Array& get_array() const;
133 const std::string& get_string() const;
134
135 template<typename T>
136 bool is() const;
137
138 bool is_null() const;
139 bool is_string() const;
140 bool is_object() const;
141 bool is_array() const;
142 bool is_bool() const;
143 bool is_integer() const;
144 bool is_number() const;
145
146 Value& operator[](const std::string& s);
147 const Value& operator[](const std::string& s) const;
148
149 Value& operator[](const size_t i);
150 const Value& operator[](const size_t i) const;
151
152 friend std::istream& operator>>(std::istream& is, Value& v);
153
154 bool contains(const std::string& s) const;
155
156 size_t size() const;
157 void push_back(const Value& value);
158 void push_back(Value&& value);
159
160 /// @brief public dump interface
161 /// @param indent number of char (space) for indentation. If -1, no new
162 /// line will be added between values
163 /// @param indent_char char used for indentation
164 /// @return the string representation of this Value
165 std::string Dump(const int indent = -1, const char indent_char = ' ') const;
166
167 private:
168 /// @brief private dump interface
169 /// @param depth_level depth of this Value in the tree
170 /// @param indent number of char (space) for indentation
171 /// @param indent_char char used for indentation
172 /// @return the string representation of this Value
173 std::string Dump(const size_t depth_level, const int indent, const char indent_char) const;
174
176 };
177
178 /// @brief Real class declaration, just a derived class of std::vector<Value>
179 class Array : public std::vector<Value>
180 {
181 using std::vector<Value>::vector;
182 };
183
184 /// @brief Real class declaration, just a derived class of std::map<std::string, Value>
185 class Object : public std::map<std::string, Value>
186 {
187 using std::map<std::string, Value>::map;
188 };
189
190 /// @brief Parse a string_view from iter for at most length characters
191 /// @param iter start character
192 /// @param length available number of characters
193 /// @param no_except if true, the function will return empty Value
194 /// instead of throwing an exception in case of unvalid string
195 /// @return The parsed Value, will throw a std::runtime_error if unvalid
196 Value Parse(std::string_view::const_iterator iter, size_t length, bool no_except = false);
197
198 /// @brief Parse a std::string
199 /// @param s string to parse
200 /// @param no_except if true, the function will return empty Value
201 /// instead of throwing an exception in case of unvalid string
202 /// @return The parsed Value, will throw a std::runtime_error if unvalid
203 Value Parse(const std::string& s, bool no_except = false);
204
205
206
207 // Templates implementations, they need to be below
208 // Object and Array class so they are not incomplete
209 // any more
210
211 template<
212 template<typename, typename> class C,
213 typename T,
214 typename A,
215 std::enable_if_t<std::is_convertible_v<T, Value>, bool>
216 >
217 Value::Value(const C<T, A>& c) : val(Array())
218 {
219 for (const auto& i : c)
220 {
221 push_back(i);
222 }
223 }
224
225 template<
226 typename T,
227 size_t N,
228 std::enable_if_t<std::is_convertible_v<T, Value>, bool>
229 >
230 Value::Value(const std::array<T, N>& v) : val(Array())
231 {
232 for (const auto& i : v)
233 {
234 push_back(i);
235 }
236 }
237
238 template<
239 typename T,
240 std::enable_if_t<std::is_convertible_v<T, Value>, bool>
241 >
242 Value::Value(const std::map<std::string, T>& m) : val(Object())
243 {
244 for (const auto& [k, v] : m)
245 {
246 operator[](k) = v;
247 }
248 }
249
250 template<
251 typename T,
252 std::enable_if_t<std::is_integral_v<T>&& std::is_unsigned_v<T>, bool>
253 >
254 Value::Value(const T u) : val(static_cast<unsigned long long int>(u))
255 {
256
257 }
258
259 template<
260 typename T,
261 std::enable_if_t<(std::is_integral_v<T>&& std::is_signed_v<T>) || std::is_enum_v<T>, bool>
262 >
263 Value::Value(const T i) : val(static_cast<long long int>(i))
264 {
265
266 }
267
268 template<
269 typename T,
270 std::enable_if_t<std::is_floating_point_v<T>, bool>
271 >
272 Value::Value(const T f) : val(static_cast<double>(f))
273 {
274
275 }
276
277 template<
278 typename T,
279 std::enable_if_t<!std::is_same_v<T, std::monostate> && !std::is_same_v<T, Object> && !std::is_same_v<T, Array> && !std::is_same_v<T, std::string>, bool>
280 >
281 T Value::get() const
282 {
283 if constexpr (std::is_same_v<T, bool>)
284 {
285 if (!std::holds_alternative<bool>(val))
286 {
287 throw std::runtime_error("Trying to get the wrong type from Json::Value");
288 }
289 return std::get<bool>(val);
290 }
291 // Wrapper to be able to get char/short/int/float
292 else if constexpr (std::is_integral_v<T> || std::is_floating_point_v<T>)
293 {
294 return get_number<T>();
295 }
296 else
297 {
298 if (!std::holds_alternative<T>(val))
299 {
300 throw std::runtime_error("Trying to get the wrong type from Json::Value");
301 }
302 return std::get<T>(val);
303 }
304 }
305
306 template<
307 typename T,
308 std::enable_if_t<std::is_integral_v<T> || std::is_floating_point_v<T>, bool>
309 >
311 {
312 if (std::holds_alternative<double>(val))
313 {
314 return static_cast<T>(std::get<double>(val));
315 }
316 else if (std::holds_alternative<long long int>(val))
317 {
318 return static_cast<T>(std::get<long long int>(val));
319 }
320 else if (std::holds_alternative<unsigned long long int>(val))
321 {
322 return static_cast<T>(std::get<unsigned long long int>(val));
323 }
324 else
325 {
326 throw std::runtime_error("Trying to get a number value from a Json::Value that is something else");
327 }
328 }
329
330 template<
331 typename T,
332 std::enable_if_t<std::is_same_v<T, Object> || std::is_same_v<T, Array> || std::is_same_v<T, std::string>, bool>
333 >
335 {
336 // special case for object (removing the RecursiveWrapper)
337 if constexpr (std::is_same_v<T, Object>)
338 {
339 if (!std::holds_alternative<ProtocolCraft::Internal::RecursiveWrapper<Object>>(val))
340 {
341 throw std::runtime_error("Trying to get the wrong type from Json::Value");
342 }
343 return std::get<ProtocolCraft::Internal::RecursiveWrapper<Object>>(val).get();
344 }
345 // special case for array (removing the RecursiveWrapper)
346 else if constexpr (std::is_same_v<T, Array>)
347 {
348 if (!std::holds_alternative<ProtocolCraft::Internal::RecursiveWrapper<Array>>(val))
349 {
350 throw std::runtime_error("Trying to get the wrong type from Json::Value");
351 }
352 return std::get<ProtocolCraft::Internal::RecursiveWrapper<Array>>(val).get();
353 }
354 // all other cases (only std::string for now) should work with generic template
355 else
356 {
357 if (!std::holds_alternative<T>(val))
358 {
359 throw std::runtime_error("Trying to get the wrong type from Json::Value");
360 }
361 return std::get<T>(val);
362 }
363 }
364
365 template<
366 typename T,
367 std::enable_if_t<std::is_same_v<T, Object> || std::is_same_v<T, Array> || std::is_same_v<T, std::string>, bool>
368 >
369 const T& Value::get() const
370 {
371 // special case for object (removing the RecursiveWrapper)
372 if constexpr (std::is_same_v<T, Object>)
373 {
374 if (!std::holds_alternative<ProtocolCraft::Internal::RecursiveWrapper<Object>>(val))
375 {
376 throw std::runtime_error("Trying to get the wrong type from Json::Value");
377 }
378 return std::get<ProtocolCraft::Internal::RecursiveWrapper<Object>>(val).get();
379 }
380 // special case for array (removing the RecursiveWrapper)
381 else if constexpr (std::is_same_v<T, Array>)
382 {
383 if (!std::holds_alternative<ProtocolCraft::Internal::RecursiveWrapper<Array>>(val))
384 {
385 throw std::runtime_error("Trying to get the wrong type from Json::Value");
386 }
387 return std::get<ProtocolCraft::Internal::RecursiveWrapper<Array>>(val).get();
388 }
389 // all other cases (only std::string for now) should work with generic template
390 else
391 {
392 if (!std::holds_alternative<T>(val))
393 {
394 throw std::runtime_error("Trying to get the wrong type from Json::Value");
395 }
396 return std::get<T>(val);
397 }
398 }
399
400 template<typename T>
401 bool Value::is() const
402 {
403 if constexpr (std::is_same_v<T, Object>)
404 {
405 return std::holds_alternative<ProtocolCraft::Internal::RecursiveWrapper<Object>>(val);
406 }
407 else if constexpr (std::is_same_v<T, Array>)
408 {
409 return std::holds_alternative<ProtocolCraft::Internal::RecursiveWrapper<Array>>(val);
410 }
411 else
412 {
413 return std::holds_alternative<T>(val);
414 }
415 }
416 }
417}
Template magic to have a full type instead of an incomplete one as required for example by std::varia...
Real class declaration, just a derived class of std::vector<Value>
Definition Json.hpp:180
Real class declaration, just a derived class of std::map<std::string, Value>
Definition Json.hpp:186
Main class, basically a JsonVariant with extra utility functions it doesn't inherit JsonVariant direc...
Definition Json.hpp:45
bool is_string() const
Definition Json.cpp:144
size_t size() const
Definition Json.cpp:237
friend std::istream & operator>>(std::istream &is, Value &v)
Definition Json.cpp:222
bool is_number() const
Definition Json.cpp:170
Internal::JsonVariant val
Definition Json.hpp:175
std::string Dump(const int indent=-1, const char indent_char=' ') const
public dump interface
Definition Json.cpp:287
bool is_integer() const
Definition Json.cpp:164
bool is_array() const
Definition Json.cpp:154
void push_back(const Value &value)
Definition Json.cpp:257
Value & operator[](const std::string &s)
Definition Json.cpp:177
Value(std::nullptr_t=nullptr)
Definition Json.cpp:24
bool is_object() const
Definition Json.cpp:149
bool contains(const std::string &s) const
Definition Json.cpp:232
std::string & get_string()
Definition Json.cpp:119
std::variant< std::monostate, ProtocolCraft::Internal::RecursiveWrapper< Object >, ProtocolCraft::Internal::RecursiveWrapper< Array >, std::string, bool, long long int, unsigned long long int, double > JsonVariant
std::variant holding the actual data
Definition Json.hpp:37
Value Parse(std::string_view::const_iterator iter, size_t length, bool no_except=false)
Parse a string_view from iter for at most length characters.
Definition Json.cpp:390