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
37namespace liong {
38namespace json {
39
40// Any error occured during JSON serialization/deserialization.
41class 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.
54enum 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
64struct JsonValue;
65
66class 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
82class 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.
99struct 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.
109struct 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.
123struct 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.
357JsonValue parse(const char *beg, const char *end);
358JsonValue 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.
362bool try_parse(const std::string &json_lit, JsonValue &out);
363
364std::string print(const JsonValue &json);
365
366} // namespace json
367} // namespace liong
368