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