Mercurial > irccd
annotate common/json.cpp @ 46:131551c901ae
Plugin logger: reorder keywords
author | David Demelier <markand@malikania.fr> |
---|---|
date | Mon, 29 Feb 2016 13:12:15 +0100 |
parents | 03068f5ed79d |
children |
rev | line source |
---|---|
0 | 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 { | |
23
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
36 if (json_is_null(v)) |
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
37 return Value(nullptr); |
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
38 if (json_is_string(v)) |
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
39 return Value(json_string_value(v)); |
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
40 if (json_is_real(v)) |
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
41 return Value(json_number_value(v)); |
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
42 if (json_is_integer(v)) |
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
43 return Value(static_cast<int>(json_integer_value(v))); |
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
44 if (json_is_boolean(v)) |
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
45 return Value(json_boolean_value(v)); |
0 | 46 if (json_is_object(v)) { |
23
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
47 Value object(Type::Object); |
0 | 48 |
49 readObject(object, v); | |
50 | |
51 return object; | |
52 } | |
53 if (json_is_array(v)) { | |
23
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
54 Value array(Type::Array); |
0 | 55 |
56 readArray(array, v); | |
57 | |
58 return array; | |
59 } | |
60 | |
23
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
61 return Value(); |
0 | 62 } |
63 | |
64 void readObject(Value &parent, json_t *object) | |
65 { | |
66 const char *key; | |
67 json_t *value; | |
68 | |
23
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
69 json_object_foreach(object, key, value) |
0 | 70 parent.insert(key, readValue(value)); |
71 } | |
72 | |
73 void readArray(Value &parent, json_t *array) | |
74 { | |
75 size_t index; | |
76 json_t *value; | |
77 | |
23
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
78 json_array_foreach(array, index, value) |
0 | 79 parent.append(readValue(value)); |
80 } | |
81 | |
82 template <typename Func, typename... Args> | |
83 Value convert(Func fn, Args&&... args) | |
84 { | |
85 json_error_t error; | |
86 json_t *json = fn(std::forward<Args>(args)..., &error); | |
87 | |
23
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
88 if (json == nullptr) |
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
89 throw Error(error.text, error.source, error.line, error.column, error.position); |
0 | 90 |
91 Value value; | |
92 | |
93 if (json_is_object(json)) { | |
23
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
94 value = Value(Type::Object); |
0 | 95 readObject(value, json); |
96 } else { | |
23
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
97 value = Value(Type::Array); |
0 | 98 readArray(value, json); |
99 } | |
100 | |
101 json_decref(json); | |
102 | |
103 return value; | |
104 } | |
105 | |
106 std::string indent(int param, int level) | |
107 { | |
108 std::string str; | |
109 | |
23
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
110 if (param < 0) |
0 | 111 str = std::string(level, '\t'); |
23
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
112 else if (param > 0) |
0 | 113 str = std::string(param * level, ' '); |
114 | |
115 return str; | |
116 } | |
117 | |
118 } // !namespace | |
119 | |
120 void Value::copy(const Value &other) | |
121 { | |
122 switch (other.m_type) { | |
123 case Type::Array: | |
124 new (&m_array) std::vector<Value>(other.m_array); | |
125 break; | |
126 case Type::Boolean: | |
127 m_boolean = other.m_boolean; | |
128 break; | |
129 case Type::Int: | |
130 m_integer = other.m_integer; | |
131 break; | |
132 case Type::Object: | |
133 new (&m_object) std::map<std::string, Value>(other.m_object); | |
134 break; | |
135 case Type::Real: | |
136 m_number = other.m_number; | |
137 break; | |
138 case Type::String: | |
139 new (&m_string) std::string(other.m_string); | |
140 break; | |
141 default: | |
142 break; | |
143 } | |
144 | |
145 m_type = other.m_type; | |
146 } | |
147 | |
148 void Value::move(Value &&other) | |
149 { | |
150 switch (other.m_type) { | |
151 case Type::Array: | |
152 new (&m_array) std::vector<Value>(std::move(other.m_array)); | |
153 break; | |
154 case Type::Boolean: | |
155 m_boolean = other.m_boolean; | |
156 break; | |
157 case Type::Int: | |
158 m_integer = other.m_integer; | |
159 break; | |
160 case Type::Object: | |
161 new (&m_object) std::map<std::string, Value>(std::move(other.m_object)); | |
162 break; | |
163 case Type::Real: | |
164 m_number = other.m_number; | |
165 break; | |
166 case Type::String: | |
167 new (&m_string) std::string(std::move(other.m_string)); | |
168 break; | |
169 default: | |
170 break; | |
171 } | |
172 | |
173 m_type = other.m_type; | |
174 } | |
175 | |
176 Value::Value(Type type) | |
23
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
177 : m_type(type) |
0 | 178 { |
179 switch (m_type) { | |
180 case Type::Array: | |
181 new (&m_array) std::vector<Value>(); | |
182 break; | |
183 case Type::Boolean: | |
184 m_boolean = false; | |
185 break; | |
186 case Type::Int: | |
187 m_integer = 0; | |
188 break; | |
189 case Type::Object: | |
190 new (&m_object) std::map<std::string, Value>(); | |
191 break; | |
192 case Type::Real: | |
193 m_number = 0; | |
194 break; | |
195 case Type::String: | |
196 new (&m_string) std::string(); | |
197 break; | |
198 default: | |
199 break; | |
200 } | |
201 } | |
202 | |
203 Value::~Value() | |
204 { | |
205 switch (m_type) { | |
206 case Type::Array: | |
207 m_array.~vector<Value>(); | |
208 break; | |
209 case Type::Object: | |
210 m_object.~map<std::string, Value>(); | |
211 break; | |
212 case Type::String: | |
213 m_string.~basic_string(); | |
214 break; | |
215 default: | |
216 break; | |
217 } | |
218 } | |
219 | |
220 bool Value::toBool() const noexcept | |
221 { | |
23
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
222 if (m_type != Type::Boolean) |
0 | 223 return false; |
224 | |
225 return m_boolean; | |
226 } | |
227 | |
228 double Value::toReal() const noexcept | |
229 { | |
23
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
230 if (m_type != Type::Real) |
0 | 231 return 0; |
232 | |
233 return m_number; | |
234 } | |
235 | |
236 int Value::toInt() const noexcept | |
237 { | |
23
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
238 if (m_type != Type::Int) |
0 | 239 return 0; |
240 | |
241 return m_integer; | |
242 } | |
243 | |
244 std::string Value::toString(bool coerce) const noexcept | |
245 { | |
246 std::string result; | |
247 | |
23
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
248 if (m_type == Type::String) |
0 | 249 result = m_string; |
23
03068f5ed79d
Misc: various style issues, #419
David Demelier <markand@malikania.fr>
parents:
0
diff
changeset
|
250 else if (coerce) |
0 | 251 result = toJson(); |
252 | |
253 return result; | |
254 } | |
255 | |
256 Value::Value(const Buffer &buffer) | |
257 { | |
258 *this = convert(json_loads, buffer.text.c_str(), 0); | |
259 } | |
260 | |
261 Value::Value(const File &file) | |
262 { | |
263 *this = convert(json_load_file, file.path.c_str(), 0); | |
264 } | |
265 | |
266 std::string Value::toJson(int level, int current) const | |
267 { | |
268 std::ostringstream oss; | |
269 | |
270 switch (m_type) { | |
271 case Type::Array: { | |
272 oss << '[' << (level != 0 ? "\n" : ""); | |
273 | |
274 unsigned total = m_array.size(); | |
275 unsigned i = 0; | |
276 for (const auto &v : m_array) { | |
277 oss << indent(level, current + 1) << v.toJson(level, current + 1); | |
278 oss << (++i < total ? "," : ""); | |
279 oss << (level != 0 ? "\n" : ""); | |
280 } | |
281 | |
282 oss << (level != 0 ? indent(level, current) : "") << ']'; | |
283 break; | |
284 } | |
285 case Type::Boolean: | |
286 oss << (m_boolean ? "true" : "false"); | |
287 break; | |
288 case Type::Int: | |
289 oss << m_integer; | |
290 break; | |
291 case Type::Null: | |
292 oss << "null"; | |
293 break; | |
294 case Type::Object: { | |
295 oss << '{' << (level != 0 ? "\n" : ""); | |
296 | |
297 unsigned total = m_object.size(); | |
298 unsigned i = 0; | |
299 for (const auto &pair : m_object) { | |
300 oss << indent(level, current + 1); | |
301 | |
302 /* Key and : */ | |
303 oss << "\"" << pair.first << "\":" << (level != 0 ? " " : ""); | |
304 | |
305 /* Value */ | |
306 oss << pair.second.toJson(level, current + 1); | |
307 | |
308 /* Comma, new line if needed */ | |
309 oss << (++i < total ? "," : "") << (level != 0 ? "\n" : ""); | |
310 } | |
311 | |
312 oss << (level != 0 ? indent(level, current) : "") << '}'; | |
313 break; | |
314 } | |
315 case Type::Real: | |
316 oss << m_number; | |
317 break; | |
318 case Type::String: | |
319 oss << "\"" << escape(m_string) << "\""; | |
320 break; | |
321 default: | |
322 break; | |
323 } | |
324 | |
325 return oss.str(); | |
326 } | |
327 | |
328 std::string escape(const std::string &value) | |
329 { | |
330 std::string result; | |
331 | |
332 for (auto it = value.begin(); it != value.end(); ++it) { | |
333 switch (*it) { | |
334 case '\\': | |
335 result += "\\\\"; | |
336 break; | |
337 case '/': | |
338 result += "\\/"; | |
339 break; | |
340 case '"': | |
341 result += "\\\""; | |
342 break; | |
343 case '\b': | |
344 result += "\\b"; | |
345 break; | |
346 case '\f': | |
347 result += "\\f"; | |
348 break; | |
349 case '\n': | |
350 result += "\\n"; | |
351 break; | |
352 case '\r': | |
353 result += "\\r"; | |
354 break; | |
355 case '\t': | |
356 result += "\\t"; | |
357 break; | |
358 default: | |
359 result += *it; | |
360 break; | |
361 } | |
362 } | |
363 | |
364 return result; | |
365 } | |
366 | |
367 } // !json | |
368 | |
369 } // !irccd |