Mercurial > irccd
comparison common/json.cpp @ 0:1158cffe5a5e
Initial import
author | David Demelier <markand@malikania.fr> |
---|---|
date | Mon, 08 Feb 2016 16:43:14 +0100 |
parents | |
children | 03068f5ed79d |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:1158cffe5a5e |
---|---|
1 /* | |
2 * json.cpp -- C++14 JSON manipulation using jansson parser | |
3 * | |
4 * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> | |
5 * | |
6 * Permission to use, copy, modify, and/or distribute this software for any | |
7 * purpose with or without fee is hereby granted, provided that the above | |
8 * copyright notice and this permission notice appear in all copies. | |
9 * | |
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
17 */ | |
18 | |
19 #include <jansson.h> | |
20 | |
21 #include <sstream> | |
22 | |
23 #include "json.h" | |
24 | |
25 namespace irccd { | |
26 | |
27 namespace json { | |
28 | |
29 namespace { | |
30 | |
31 void readObject(Value &parent, json_t *object); | |
32 void readArray(Value &parent, json_t *array); | |
33 | |
34 Value readValue(json_t *v) | |
35 { | |
36 if (json_is_null(v)) { | |
37 return Value{nullptr}; | |
38 } | |
39 if (json_is_string(v)) { | |
40 return Value{json_string_value(v)}; | |
41 } | |
42 if (json_is_real(v)) { | |
43 return Value{json_number_value(v)}; | |
44 } | |
45 if (json_is_integer(v)) { | |
46 return Value{static_cast<int>(json_integer_value(v))}; | |
47 } | |
48 if (json_is_boolean(v)) { | |
49 return Value{json_boolean_value(v)}; | |
50 } | |
51 if (json_is_object(v)) { | |
52 Value object{Type::Object}; | |
53 | |
54 readObject(object, v); | |
55 | |
56 return object; | |
57 } | |
58 if (json_is_array(v)) { | |
59 Value array{Type::Array}; | |
60 | |
61 readArray(array, v); | |
62 | |
63 return array; | |
64 } | |
65 | |
66 return Value{}; | |
67 } | |
68 | |
69 void readObject(Value &parent, json_t *object) | |
70 { | |
71 const char *key; | |
72 json_t *value; | |
73 | |
74 json_object_foreach(object, key, value) { | |
75 parent.insert(key, readValue(value)); | |
76 } | |
77 } | |
78 | |
79 void readArray(Value &parent, json_t *array) | |
80 { | |
81 size_t index; | |
82 json_t *value; | |
83 | |
84 json_array_foreach(array, index, value) { | |
85 parent.append(readValue(value)); | |
86 } | |
87 } | |
88 | |
89 template <typename Func, typename... Args> | |
90 Value convert(Func fn, Args&&... args) | |
91 { | |
92 json_error_t error; | |
93 json_t *json = fn(std::forward<Args>(args)..., &error); | |
94 | |
95 if (json == nullptr) { | |
96 throw Error{error.text, error.source, error.line, error.column, error.position}; | |
97 } | |
98 | |
99 Value value; | |
100 | |
101 if (json_is_object(json)) { | |
102 value = Value{Type::Object}; | |
103 readObject(value, json); | |
104 } else { | |
105 value = Value{Type::Array}; | |
106 readArray(value, json); | |
107 } | |
108 | |
109 json_decref(json); | |
110 | |
111 return value; | |
112 } | |
113 | |
114 std::string indent(int param, int level) | |
115 { | |
116 std::string str; | |
117 | |
118 if (param < 0) { | |
119 str = std::string(level, '\t'); | |
120 } else if (param > 0) { | |
121 str = std::string(param * level, ' '); | |
122 } | |
123 | |
124 return str; | |
125 } | |
126 | |
127 } // !namespace | |
128 | |
129 void Value::copy(const Value &other) | |
130 { | |
131 switch (other.m_type) { | |
132 case Type::Array: | |
133 new (&m_array) std::vector<Value>(other.m_array); | |
134 break; | |
135 case Type::Boolean: | |
136 m_boolean = other.m_boolean; | |
137 break; | |
138 case Type::Int: | |
139 m_integer = other.m_integer; | |
140 break; | |
141 case Type::Object: | |
142 new (&m_object) std::map<std::string, Value>(other.m_object); | |
143 break; | |
144 case Type::Real: | |
145 m_number = other.m_number; | |
146 break; | |
147 case Type::String: | |
148 new (&m_string) std::string(other.m_string); | |
149 break; | |
150 default: | |
151 break; | |
152 } | |
153 | |
154 m_type = other.m_type; | |
155 } | |
156 | |
157 void Value::move(Value &&other) | |
158 { | |
159 switch (other.m_type) { | |
160 case Type::Array: | |
161 new (&m_array) std::vector<Value>(std::move(other.m_array)); | |
162 break; | |
163 case Type::Boolean: | |
164 m_boolean = other.m_boolean; | |
165 break; | |
166 case Type::Int: | |
167 m_integer = other.m_integer; | |
168 break; | |
169 case Type::Object: | |
170 new (&m_object) std::map<std::string, Value>(std::move(other.m_object)); | |
171 break; | |
172 case Type::Real: | |
173 m_number = other.m_number; | |
174 break; | |
175 case Type::String: | |
176 new (&m_string) std::string(std::move(other.m_string)); | |
177 break; | |
178 default: | |
179 break; | |
180 } | |
181 | |
182 m_type = other.m_type; | |
183 } | |
184 | |
185 Value::Value(Type type) | |
186 : m_type{type} | |
187 { | |
188 switch (m_type) { | |
189 case Type::Array: | |
190 new (&m_array) std::vector<Value>(); | |
191 break; | |
192 case Type::Boolean: | |
193 m_boolean = false; | |
194 break; | |
195 case Type::Int: | |
196 m_integer = 0; | |
197 break; | |
198 case Type::Object: | |
199 new (&m_object) std::map<std::string, Value>(); | |
200 break; | |
201 case Type::Real: | |
202 m_number = 0; | |
203 break; | |
204 case Type::String: | |
205 new (&m_string) std::string(); | |
206 break; | |
207 default: | |
208 break; | |
209 } | |
210 } | |
211 | |
212 Value::~Value() | |
213 { | |
214 switch (m_type) { | |
215 case Type::Array: | |
216 m_array.~vector<Value>(); | |
217 break; | |
218 case Type::Object: | |
219 m_object.~map<std::string, Value>(); | |
220 break; | |
221 case Type::String: | |
222 m_string.~basic_string(); | |
223 break; | |
224 default: | |
225 break; | |
226 } | |
227 } | |
228 | |
229 bool Value::toBool() const noexcept | |
230 { | |
231 if (m_type != Type::Boolean) { | |
232 return false; | |
233 } | |
234 | |
235 return m_boolean; | |
236 } | |
237 | |
238 double Value::toReal() const noexcept | |
239 { | |
240 if (m_type != Type::Real) { | |
241 return 0; | |
242 } | |
243 | |
244 return m_number; | |
245 } | |
246 | |
247 int Value::toInt() const noexcept | |
248 { | |
249 if (m_type != Type::Int) { | |
250 return 0; | |
251 } | |
252 | |
253 return m_integer; | |
254 } | |
255 | |
256 std::string Value::toString(bool coerce) const noexcept | |
257 { | |
258 std::string result; | |
259 | |
260 if (m_type == Type::String) { | |
261 result = m_string; | |
262 } else if (coerce) { | |
263 result = toJson(); | |
264 } | |
265 | |
266 return result; | |
267 } | |
268 | |
269 Value::Value(const Buffer &buffer) | |
270 { | |
271 *this = convert(json_loads, buffer.text.c_str(), 0); | |
272 } | |
273 | |
274 Value::Value(const File &file) | |
275 { | |
276 *this = convert(json_load_file, file.path.c_str(), 0); | |
277 } | |
278 | |
279 std::string Value::toJson(int level, int current) const | |
280 { | |
281 std::ostringstream oss; | |
282 | |
283 switch (m_type) { | |
284 case Type::Array: { | |
285 oss << '[' << (level != 0 ? "\n" : ""); | |
286 | |
287 unsigned total = m_array.size(); | |
288 unsigned i = 0; | |
289 for (const auto &v : m_array) { | |
290 oss << indent(level, current + 1) << v.toJson(level, current + 1); | |
291 oss << (++i < total ? "," : ""); | |
292 oss << (level != 0 ? "\n" : ""); | |
293 } | |
294 | |
295 oss << (level != 0 ? indent(level, current) : "") << ']'; | |
296 break; | |
297 } | |
298 case Type::Boolean: | |
299 oss << (m_boolean ? "true" : "false"); | |
300 break; | |
301 case Type::Int: | |
302 oss << m_integer; | |
303 break; | |
304 case Type::Null: | |
305 oss << "null"; | |
306 break; | |
307 case Type::Object: { | |
308 oss << '{' << (level != 0 ? "\n" : ""); | |
309 | |
310 unsigned total = m_object.size(); | |
311 unsigned i = 0; | |
312 for (const auto &pair : m_object) { | |
313 oss << indent(level, current + 1); | |
314 | |
315 /* Key and : */ | |
316 oss << "\"" << pair.first << "\":" << (level != 0 ? " " : ""); | |
317 | |
318 /* Value */ | |
319 oss << pair.second.toJson(level, current + 1); | |
320 | |
321 /* Comma, new line if needed */ | |
322 oss << (++i < total ? "," : "") << (level != 0 ? "\n" : ""); | |
323 } | |
324 | |
325 oss << (level != 0 ? indent(level, current) : "") << '}'; | |
326 break; | |
327 } | |
328 case Type::Real: | |
329 oss << m_number; | |
330 break; | |
331 case Type::String: | |
332 oss << "\"" << escape(m_string) << "\""; | |
333 break; | |
334 default: | |
335 break; | |
336 } | |
337 | |
338 return oss.str(); | |
339 } | |
340 | |
341 std::string escape(const std::string &value) | |
342 { | |
343 std::string result; | |
344 | |
345 for (auto it = value.begin(); it != value.end(); ++it) { | |
346 switch (*it) { | |
347 case '\\': | |
348 result += "\\\\"; | |
349 break; | |
350 case '/': | |
351 result += "\\/"; | |
352 break; | |
353 case '"': | |
354 result += "\\\""; | |
355 break; | |
356 case '\b': | |
357 result += "\\b"; | |
358 break; | |
359 case '\f': | |
360 result += "\\f"; | |
361 break; | |
362 case '\n': | |
363 result += "\\n"; | |
364 break; | |
365 case '\r': | |
366 result += "\\r"; | |
367 break; | |
368 case '\t': | |
369 result += "\\t"; | |
370 break; | |
371 default: | |
372 result += *it; | |
373 break; | |
374 } | |
375 } | |
376 | |
377 return result; | |
378 } | |
379 | |
380 } // !json | |
381 | |
382 } // !irccd |