1 | // Adapted from https://github.com/PENGUINLIONG/graphi-t |
2 | |
3 | // Copyright (c) 2019 Rendong Liang |
4 | // |
5 | // Permission is hereby granted, free of charge, to any |
6 | // person obtaining a copy of this software and associated |
7 | // documentation files (the "Software"), to deal in the |
8 | // Software without restriction, including without |
9 | // limitation the rights to use, copy, modify, merge, |
10 | // publish, distribute, sublicense, and/or sell copies of |
11 | // the Software, and to permit persons to whom the Software |
12 | // is furnished to do so, subject to the following |
13 | // conditions: |
14 | // |
15 | // The above copyright notice and this permission notice |
16 | // shall be included in all copies or substantial portions |
17 | // of the Software. |
18 | // |
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF |
20 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED |
21 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A |
22 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT |
23 | // SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
24 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
25 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR |
26 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
27 | // DEALINGS IN THE SOFTWARE. |
28 | |
29 | // JSON serialization/deserialization. |
30 | // @PENGUINLIONG |
31 | #pragma once |
32 | #include <string> |
33 | #include <vector> |
34 | #include <map> |
35 | #include <sstream> |
36 | |
37 | namespace liong { |
38 | namespace json { |
39 | |
40 | // Any error occured during JSON serialization/deserialization. |
41 | class JsonException : public std::exception { |
42 | private: |
43 | std::string msg_; |
44 | |
45 | public: |
46 | explicit JsonException(const char *msg) : msg_(msg) { |
47 | } |
48 | const char *what() const noexcept override { |
49 | return msg_.c_str(); |
50 | } |
51 | }; |
52 | |
53 | // Type of JSON value. |
54 | enum JsonType { |
55 | L_JSON_NULL, |
56 | L_JSON_BOOLEAN, |
57 | L_JSON_FLOAT, |
58 | L_JSON_INT, |
59 | L_JSON_STRING, |
60 | L_JSON_OBJECT, |
61 | L_JSON_ARRAY, |
62 | }; |
63 | |
64 | struct JsonValue; |
65 | |
66 | class JsonElementEnumerator { |
67 | std::vector<JsonValue>::const_iterator beg_, end_; |
68 | |
69 | public: |
70 | explicit JsonElementEnumerator(const std::vector<JsonValue> &arr) |
71 | : beg_(arr.cbegin()), end_(arr.cend()) { |
72 | } |
73 | |
74 | std::vector<JsonValue>::const_iterator begin() const { |
75 | return beg_; |
76 | } |
77 | std::vector<JsonValue>::const_iterator end() const { |
78 | return end_; |
79 | } |
80 | }; |
81 | |
82 | class JsonFieldEnumerator { |
83 | std::map<std::string, JsonValue>::const_iterator beg_, end_; |
84 | |
85 | public: |
86 | explicit JsonFieldEnumerator(const std::map<std::string, JsonValue> &obj) |
87 | : beg_(obj.cbegin()), end_(obj.cend()) { |
88 | } |
89 | |
90 | std::map<std::string, JsonValue>::const_iterator begin() const { |
91 | return beg_; |
92 | } |
93 | std::map<std::string, JsonValue>::const_iterator end() const { |
94 | return end_; |
95 | } |
96 | }; |
97 | |
98 | // JSON array builder. |
99 | struct JsonArray { |
100 | std::vector<JsonValue> inner; |
101 | |
102 | inline JsonArray() = default; |
103 | inline explicit JsonArray(std::vector<JsonValue> &&b) : inner(std::move(b)) { |
104 | } |
105 | inline JsonArray(std::initializer_list<JsonValue> &&elems) : inner(elems) { |
106 | } |
107 | }; |
108 | // JSON object builder. |
109 | struct JsonObject { |
110 | std::map<std::string, JsonValue> inner; |
111 | |
112 | inline JsonObject() = default; |
113 | inline explicit JsonObject(std::map<std::string, JsonValue> &&b) |
114 | : inner(std::move(b)) { |
115 | } |
116 | inline JsonObject( |
117 | std::initializer_list<std::pair<const std::string, JsonValue>> &&fields) |
118 | : inner(fields) { |
119 | } |
120 | }; |
121 | |
122 | // Represent a abstract value in JSON representation. |
123 | struct JsonValue { |
124 | JsonType ty; |
125 | bool b; |
126 | int64_t num_int; |
127 | double num_float; |
128 | std::string str; |
129 | JsonObject obj; |
130 | JsonArray arr; |
131 | |
132 | inline explicit JsonValue() : ty(L_JSON_NULL) { |
133 | } |
134 | inline explicit JsonValue(std::nullptr_t) : ty(L_JSON_NULL) { |
135 | } |
136 | inline explicit JsonValue(bool b) : ty(L_JSON_BOOLEAN), b(b) { |
137 | } |
138 | inline explicit JsonValue(double num) : ty(L_JSON_FLOAT), num_float(num) { |
139 | } |
140 | inline explicit JsonValue(float num) : ty(L_JSON_FLOAT), num_float(num) { |
141 | } |
142 | inline explicit JsonValue(char num) : ty(L_JSON_INT), num_int(num) { |
143 | } |
144 | inline explicit JsonValue(signed char num) : ty(L_JSON_INT), num_int(num) { |
145 | } |
146 | inline explicit JsonValue(unsigned char num) : ty(L_JSON_INT), num_int(num) { |
147 | } |
148 | inline explicit JsonValue(short num) : ty(L_JSON_INT), num_int(num) { |
149 | } |
150 | inline explicit JsonValue(unsigned short num) : ty(L_JSON_INT), num_int(num) { |
151 | } |
152 | inline explicit JsonValue(int num) : ty(L_JSON_INT), num_int(num) { |
153 | } |
154 | inline explicit JsonValue(unsigned int num) : ty(L_JSON_INT), num_int(num) { |
155 | } |
156 | inline explicit JsonValue(long num) : ty(L_JSON_INT), num_int(num) { |
157 | } |
158 | inline explicit JsonValue(unsigned long num) : ty(L_JSON_INT), num_int(num) { |
159 | } |
160 | inline explicit JsonValue(long long num) : ty(L_JSON_INT), num_int(num) { |
161 | } |
162 | inline explicit JsonValue(unsigned long long num) |
163 | : ty(L_JSON_INT), num_int(num) { |
164 | } |
165 | inline explicit JsonValue(const char *str) : ty(L_JSON_STRING), str(str) { |
166 | } |
167 | inline explicit JsonValue(const std::string &str) |
168 | : ty(L_JSON_STRING), str(str) { |
169 | } |
170 | inline explicit JsonValue(std::string &&str) |
171 | : ty(L_JSON_STRING), str(std::forward<std::string>(str)) { |
172 | } |
173 | inline explicit JsonValue(JsonObject &&obj) |
174 | : ty(L_JSON_OBJECT), obj(std::move(obj.inner)) { |
175 | } |
176 | inline explicit JsonValue(JsonArray &&arr) |
177 | : ty(L_JSON_ARRAY), arr(move(arr.inner)) { |
178 | } |
179 | |
180 | inline JsonValue &operator[](const char *key) { |
181 | if (!is_obj()) { |
182 | throw JsonException("value is not an object" ); |
183 | } |
184 | return obj.inner.at(key); |
185 | } |
186 | inline const JsonValue &operator[](const char *key) const { |
187 | if (!is_obj()) { |
188 | throw JsonException("value is not an object" ); |
189 | } |
190 | return obj.inner.at(key); |
191 | } |
192 | inline JsonValue &operator[](const std::string &key) { |
193 | if (!is_obj()) { |
194 | throw JsonException("value is not an object" ); |
195 | } |
196 | return obj.inner.at(key); |
197 | } |
198 | inline const JsonValue &operator[](const std::string &key) const { |
199 | if (!is_obj()) { |
200 | throw JsonException("value is not an object" ); |
201 | } |
202 | return obj.inner.at(key); |
203 | } |
204 | inline JsonValue &operator[](size_t i) { |
205 | if (!is_arr()) { |
206 | throw JsonException("value is not an array" ); |
207 | } |
208 | return arr.inner.at(i); |
209 | } |
210 | inline const JsonValue &operator[](size_t i) const { |
211 | if (!is_arr()) { |
212 | throw JsonException("value is not an array" ); |
213 | } |
214 | return arr.inner.at(i); |
215 | } |
216 | inline explicit operator bool() const { |
217 | if (!is_bool()) { |
218 | throw JsonException("value is not a bool" ); |
219 | } |
220 | return b; |
221 | } |
222 | inline explicit operator double() const { |
223 | if (!is_num()) { |
224 | throw JsonException("value is not a number" ); |
225 | } |
226 | return num_float; |
227 | } |
228 | inline explicit operator float() const { |
229 | if (!is_num()) { |
230 | throw JsonException("value is not a number" ); |
231 | } |
232 | return (float)num_float; |
233 | } |
234 | inline explicit operator char() const { |
235 | if (!is_num()) { |
236 | throw JsonException("value is not a number" ); |
237 | } |
238 | return (char)num_int; |
239 | } |
240 | inline explicit operator signed char() const { |
241 | if (!is_num()) { |
242 | throw JsonException("value is not a number" ); |
243 | } |
244 | return (signed char)num_int; |
245 | } |
246 | inline explicit operator unsigned char() const { |
247 | if (!is_num()) { |
248 | throw JsonException("value is not a number" ); |
249 | } |
250 | return (unsigned char)num_int; |
251 | } |
252 | inline explicit operator short() const { |
253 | if (!is_num()) { |
254 | throw JsonException("value is not a number" ); |
255 | } |
256 | return (short)num_int; |
257 | } |
258 | inline explicit operator unsigned short() const { |
259 | if (!is_num()) { |
260 | throw JsonException("value is not a number" ); |
261 | } |
262 | return (unsigned short)num_int; |
263 | } |
264 | inline explicit operator int() const { |
265 | if (!is_num()) { |
266 | throw JsonException("value is not a number" ); |
267 | } |
268 | return (int)num_int; |
269 | } |
270 | inline explicit operator unsigned int() const { |
271 | if (!is_num()) { |
272 | throw JsonException("value is not a number" ); |
273 | } |
274 | return (unsigned int)num_int; |
275 | } |
276 | inline explicit operator long() const { |
277 | if (!is_num()) { |
278 | throw JsonException("value is not a number" ); |
279 | } |
280 | return (long)num_int; |
281 | } |
282 | inline explicit operator unsigned long() const { |
283 | if (!is_num()) { |
284 | throw JsonException("value is not a number" ); |
285 | } |
286 | return (unsigned long)num_int; |
287 | } |
288 | inline explicit operator long long() const { |
289 | if (!is_num()) { |
290 | throw JsonException("value is not a number" ); |
291 | } |
292 | return (long long)num_int; |
293 | } |
294 | inline explicit operator unsigned long long() const { |
295 | if (!is_num()) { |
296 | throw JsonException("value is not a number" ); |
297 | } |
298 | return (unsigned long long)num_int; |
299 | } |
300 | inline explicit operator const std::string &() const { |
301 | if (!is_str()) { |
302 | throw JsonException("value is not a string" ); |
303 | } |
304 | return str; |
305 | } |
306 | inline explicit operator const JsonArray &() const { |
307 | if (!is_arr()) { |
308 | throw JsonException("value is not an array" ); |
309 | } |
310 | return arr; |
311 | } |
312 | inline explicit operator const JsonObject &() const { |
313 | if (!is_obj()) { |
314 | throw JsonException("value is not an object" ); |
315 | } |
316 | return obj; |
317 | } |
318 | |
319 | inline bool is_null() const { |
320 | return ty == L_JSON_NULL; |
321 | } |
322 | inline bool is_bool() const { |
323 | return ty == L_JSON_BOOLEAN; |
324 | } |
325 | inline bool is_num() const { |
326 | return ty == L_JSON_FLOAT || ty == L_JSON_INT; |
327 | } |
328 | inline bool is_str() const { |
329 | return ty == L_JSON_STRING; |
330 | } |
331 | inline bool is_obj() const { |
332 | return ty == L_JSON_OBJECT; |
333 | } |
334 | inline bool is_arr() const { |
335 | return ty == L_JSON_ARRAY; |
336 | } |
337 | |
338 | inline size_t size() const { |
339 | if (is_obj()) { |
340 | return obj.inner.size(); |
341 | } else if (is_arr()) { |
342 | return arr.inner.size(); |
343 | } else { |
344 | throw JsonException("only object and array can have size" ); |
345 | } |
346 | } |
347 | inline JsonElementEnumerator elems() const { |
348 | return JsonElementEnumerator(arr.inner); |
349 | } |
350 | inline JsonFieldEnumerator fields() const { |
351 | return JsonFieldEnumerator(obj.inner); |
352 | } |
353 | }; |
354 | |
355 | // Parse JSON literal into and `JsonValue` object. If the JSON is invalid or |
356 | // unsupported, `JsonException` will be raised. |
357 | JsonValue parse(const char *beg, const char *end); |
358 | JsonValue parse(const std::string &json_lit); |
359 | // Returns true when JSON parsing successfully finished and parsed value is |
360 | // returned via `out`. Otherwise, false is returned and out contains incomplete |
361 | // result. |
362 | bool try_parse(const std::string &json_lit, JsonValue &out); |
363 | |
364 | std::string print(const JsonValue &json); |
365 | |
366 | } // namespace json |
367 | } // namespace liong |
368 | |