]> granicus.if.org Git - icinga2/blob - lib/base/json.cpp
Merge pull request #7185 from Icinga/bugfix/gelfwriter-wrong-log-facility
[icinga2] / lib / base / json.cpp
1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
3 #include "base/json.hpp"
4 #include "base/debug.hpp"
5 #include "base/namespace.hpp"
6 #include "base/dictionary.hpp"
7 #include "base/array.hpp"
8 #include "base/objectlock.hpp"
9 #include "base/convert.hpp"
10 #include "base/utility.hpp"
11 #include <bitset>
12 #include <boost/exception_ptr.hpp>
13 #include <cstdint>
14 #include <json.hpp>
15 #include <stack>
16 #include <utility>
17 #include <vector>
18
19 using namespace icinga;
20
21 class JsonSax : public nlohmann::json_sax<nlohmann::json>
22 {
23 public:
24         bool null() override;
25         bool boolean(bool val) override;
26         bool number_integer(number_integer_t val) override;
27         bool number_unsigned(number_unsigned_t val) override;
28         bool number_float(number_float_t val, const string_t& s) override;
29         bool string(string_t& val) override;
30         bool start_object(std::size_t elements) override;
31         bool key(string_t& val) override;
32         bool end_object() override;
33         bool start_array(std::size_t elements) override;
34         bool end_array() override;
35         bool parse_error(std::size_t position, const std::string& last_token, const nlohmann::detail::exception& ex) override;
36
37         Value GetResult();
38
39 private:
40         Value m_Root;
41         std::stack<std::pair<Dictionary*, Array*>> m_CurrentSubtree;
42         String m_CurrentKey;
43
44         void FillCurrentTarget(Value value);
45 };
46
47 const char l_Null[] = "null";
48 const char l_False[] = "false";
49 const char l_True[] = "true";
50 const char l_Indent[] = "    ";
51
52 // https://github.com/nlohmann/json/issues/1512
53 template<bool prettyPrint>
54 class JsonEncoder
55 {
56 public:
57         void Null();
58         void Boolean(bool value);
59         void NumberFloat(double value);
60         void Strng(String value);
61         void StartObject();
62         void Key(String value);
63         void EndObject();
64         void StartArray();
65         void EndArray();
66
67         String GetResult();
68
69 private:
70         std::vector<char> m_Result;
71         String m_CurrentKey;
72         std::stack<std::bitset<2>> m_CurrentSubtree;
73
74         void AppendChar(char c);
75
76         template<class Iterator>
77         void AppendChars(Iterator begin, Iterator end);
78
79         void AppendJson(nlohmann::json json);
80
81         void BeforeItem();
82
83         void FinishContainer(char terminator);
84 };
85
86 template<bool prettyPrint>
87 void Encode(JsonEncoder<prettyPrint>& stateMachine, const Value& value);
88
89 template<bool prettyPrint>
90 inline
91 void EncodeNamespace(JsonEncoder<prettyPrint>& stateMachine, const Namespace::Ptr& ns)
92 {
93         stateMachine.StartObject();
94
95         ObjectLock olock(ns);
96         for (const Namespace::Pair& kv : ns) {
97                 stateMachine.Key(Utility::ValidateUTF8(kv.first));
98                 Encode(stateMachine, kv.second->Get());
99         }
100
101         stateMachine.EndObject();
102 }
103
104 template<bool prettyPrint>
105 inline
106 void EncodeDictionary(JsonEncoder<prettyPrint>& stateMachine, const Dictionary::Ptr& dict)
107 {
108         stateMachine.StartObject();
109
110         ObjectLock olock(dict);
111         for (const Dictionary::Pair& kv : dict) {
112                 stateMachine.Key(Utility::ValidateUTF8(kv.first));
113                 Encode(stateMachine, kv.second);
114         }
115
116         stateMachine.EndObject();
117 }
118
119 template<bool prettyPrint>
120 inline
121 void EncodeArray(JsonEncoder<prettyPrint>& stateMachine, const Array::Ptr& arr)
122 {
123         stateMachine.StartArray();
124
125         ObjectLock olock(arr);
126         for (const Value& value : arr) {
127                 Encode(stateMachine, value);
128         }
129
130         stateMachine.EndArray();
131 }
132
133 template<bool prettyPrint>
134 void Encode(JsonEncoder<prettyPrint>& stateMachine, const Value& value)
135 {
136         switch (value.GetType()) {
137                 case ValueNumber:
138                         stateMachine.NumberFloat(value.Get<double>());
139                         break;
140
141                 case ValueBoolean:
142                         stateMachine.Boolean(value.ToBool());
143                         break;
144
145                 case ValueString:
146                         stateMachine.Strng(Utility::ValidateUTF8(value.Get<String>()));
147                         break;
148
149                 case ValueObject:
150                         {
151                                 const Object::Ptr& obj = value.Get<Object::Ptr>();
152
153                                 {
154                                         Namespace::Ptr ns = dynamic_pointer_cast<Namespace>(obj);
155                                         if (ns) {
156                                                 EncodeNamespace(stateMachine, ns);
157                                                 break;
158                                         }
159                                 }
160
161                                 {
162                                         Dictionary::Ptr dict = dynamic_pointer_cast<Dictionary>(obj);
163                                         if (dict) {
164                                                 EncodeDictionary(stateMachine, dict);
165                                                 break;
166                                         }
167                                 }
168
169                                 Array::Ptr arr = dynamic_pointer_cast<Array>(obj);
170                                 if (arr) {
171                                         EncodeArray(stateMachine, arr);
172                                         break;
173                                 }
174                         }
175
176                         stateMachine.Null();
177
178                         break;
179
180                 case ValueEmpty:
181                         stateMachine.Null();
182                         break;
183
184                 default:
185                         VERIFY(!"Invalid variant type.");
186         }
187 }
188
189 String icinga::JsonEncode(const Value& value, bool pretty_print)
190 {
191         if (pretty_print) {
192                 JsonEncoder<true> stateMachine;
193
194                 Encode(stateMachine, value);
195
196                 return stateMachine.GetResult();
197         } else {
198                 JsonEncoder<false> stateMachine;
199
200                 Encode(stateMachine, value);
201
202                 return stateMachine.GetResult();
203         }
204 }
205
206 Value icinga::JsonDecode(const String& data)
207 {
208         String sanitized (Utility::ValidateUTF8(data));
209
210         JsonSax stateMachine;
211
212         nlohmann::json::sax_parse(sanitized.Begin(), sanitized.End(), &stateMachine);
213
214         return stateMachine.GetResult();
215 }
216
217 inline
218 bool JsonSax::null()
219 {
220         FillCurrentTarget(Value());
221
222         return true;
223 }
224
225 inline
226 bool JsonSax::boolean(bool val)
227 {
228         FillCurrentTarget(val);
229
230         return true;
231 }
232
233 inline
234 bool JsonSax::number_integer(JsonSax::number_integer_t val)
235 {
236         FillCurrentTarget((double)val);
237
238         return true;
239 }
240
241 inline
242 bool JsonSax::number_unsigned(JsonSax::number_unsigned_t val)
243 {
244         FillCurrentTarget((double)val);
245
246         return true;
247 }
248
249 inline
250 bool JsonSax::number_float(JsonSax::number_float_t val, const JsonSax::string_t&)
251 {
252         FillCurrentTarget((double)val);
253
254         return true;
255 }
256
257 inline
258 bool JsonSax::string(JsonSax::string_t& val)
259 {
260         FillCurrentTarget(String(std::move(val)));
261
262         return true;
263 }
264
265 inline
266 bool JsonSax::start_object(std::size_t)
267 {
268         auto object (new Dictionary());
269
270         FillCurrentTarget(object);
271
272         m_CurrentSubtree.push({object, nullptr});
273
274         return true;
275 }
276
277 inline
278 bool JsonSax::key(JsonSax::string_t& val)
279 {
280         m_CurrentKey = String(std::move(val));
281
282         return true;
283 }
284
285 inline
286 bool JsonSax::end_object()
287 {
288         m_CurrentSubtree.pop();
289         m_CurrentKey = String();
290
291         return true;
292 }
293
294 inline
295 bool JsonSax::start_array(std::size_t)
296 {
297         auto array (new Array());
298
299         FillCurrentTarget(array);
300
301         m_CurrentSubtree.push({nullptr, array});
302
303         return true;
304 }
305
306 inline
307 bool JsonSax::end_array()
308 {
309         m_CurrentSubtree.pop();
310
311         return true;
312 }
313
314 inline
315 bool JsonSax::parse_error(std::size_t, const std::string&, const nlohmann::detail::exception& ex)
316 {
317         throw std::invalid_argument(ex.what());
318 }
319
320 inline
321 Value JsonSax::GetResult()
322 {
323         return m_Root;
324 }
325
326 inline
327 void JsonSax::FillCurrentTarget(Value value)
328 {
329         if (m_CurrentSubtree.empty()) {
330                 m_Root = value;
331         } else {
332                 auto& node (m_CurrentSubtree.top());
333
334                 if (node.first) {
335                         node.first->Set(m_CurrentKey, value);
336                 } else {
337                         node.second->Add(value);
338                 }
339         }
340 }
341
342 template<bool prettyPrint>
343 inline
344 void JsonEncoder<prettyPrint>::Null()
345 {
346         BeforeItem();
347         AppendChars((const char*)l_Null, (const char*)l_Null + 4);
348 }
349
350 template<bool prettyPrint>
351 inline
352 void JsonEncoder<prettyPrint>::Boolean(bool value)
353 {
354         BeforeItem();
355
356         if (value) {
357                 AppendChars((const char*)l_True, (const char*)l_True + 4);
358         } else {
359                 AppendChars((const char*)l_False, (const char*)l_False + 5);
360         }
361 }
362
363 template<bool prettyPrint>
364 inline
365 void JsonEncoder<prettyPrint>::NumberFloat(double value)
366 {
367         BeforeItem();
368         AppendJson(value);
369 }
370
371 template<bool prettyPrint>
372 inline
373 void JsonEncoder<prettyPrint>::Strng(String value)
374 {
375         BeforeItem();
376         AppendJson(std::move(value));
377 }
378
379 template<bool prettyPrint>
380 inline
381 void JsonEncoder<prettyPrint>::StartObject()
382 {
383         BeforeItem();
384         AppendChar('{');
385
386         m_CurrentSubtree.push(2);
387 }
388
389 template<bool prettyPrint>
390 inline
391 void JsonEncoder<prettyPrint>::Key(String value)
392 {
393         m_CurrentKey = std::move(value);
394 }
395
396 template<bool prettyPrint>
397 inline
398 void JsonEncoder<prettyPrint>::EndObject()
399 {
400         FinishContainer('}');
401 }
402
403 template<bool prettyPrint>
404 inline
405 void JsonEncoder<prettyPrint>::StartArray()
406 {
407         BeforeItem();
408         AppendChar('[');
409
410         m_CurrentSubtree.push(0);
411 }
412
413 template<bool prettyPrint>
414 inline
415 void JsonEncoder<prettyPrint>::EndArray()
416 {
417         FinishContainer(']');
418 }
419
420 template<bool prettyPrint>
421 inline
422 String JsonEncoder<prettyPrint>::GetResult()
423 {
424         return String(m_Result.begin(), m_Result.end());
425 }
426
427 template<bool prettyPrint>
428 inline
429 void JsonEncoder<prettyPrint>::AppendChar(char c)
430 {
431         m_Result.emplace_back(c);
432 }
433
434 template<bool prettyPrint>
435 template<class Iterator>
436 inline
437 void JsonEncoder<prettyPrint>::AppendChars(Iterator begin, Iterator end)
438 {
439         m_Result.insert(m_Result.end(), begin, end);
440 }
441
442 template<bool prettyPrint>
443 inline
444 void JsonEncoder<prettyPrint>::AppendJson(nlohmann::json json)
445 {
446         nlohmann::detail::serializer<nlohmann::json>(nlohmann::detail::output_adapter<char>(m_Result), ' ').dump(std::move(json), prettyPrint, true, 0);
447 }
448
449 template<bool prettyPrint>
450 inline
451 void JsonEncoder<prettyPrint>::BeforeItem()
452 {
453         if (!m_CurrentSubtree.empty()) {
454                 auto& node (m_CurrentSubtree.top());
455
456                 if (node[0]) {
457                         AppendChar(',');
458                 } else {
459                         node[0] = true;
460                 }
461
462                 if (prettyPrint) {
463                         AppendChar('\n');
464
465                         for (auto i (m_CurrentSubtree.size()); i; --i) {
466                                 AppendChars((const char*)l_Indent, (const char*)l_Indent + 4);
467                         }
468                 }
469
470                 if (node[1]) {
471                         AppendJson(std::move(m_CurrentKey));
472                         AppendChar(':');
473
474                         if (prettyPrint) {
475                                 AppendChar(' ');
476                         }
477                 }
478         }
479 }
480
481 template<bool prettyPrint>
482 inline
483 void JsonEncoder<prettyPrint>::FinishContainer(char terminator)
484 {
485         if (prettyPrint && m_CurrentSubtree.top()[0]) {
486                 AppendChar('\n');
487
488                 for (auto i (m_CurrentSubtree.size() - 1u); i; --i) {
489                         AppendChars((const char*)l_Indent, (const char*)l_Indent + 4);
490                 }
491         }
492
493         AppendChar(terminator);
494
495         m_CurrentSubtree.pop();
496 }