1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
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"
12 #include <boost/exception_ptr.hpp>
19 using namespace icinga;
21 class JsonSax : public nlohmann::json_sax<nlohmann::json>
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;
41 std::stack<std::pair<Dictionary*, Array*>> m_CurrentSubtree;
44 void FillCurrentTarget(Value value);
47 const char l_Null[] = "null";
48 const char l_False[] = "false";
49 const char l_True[] = "true";
50 const char l_Indent[] = " ";
52 // https://github.com/nlohmann/json/issues/1512
53 template<bool prettyPrint>
58 void Boolean(bool value);
59 void NumberFloat(double value);
60 void Strng(String value);
62 void Key(String value);
70 std::vector<char> m_Result;
72 std::stack<std::bitset<2>> m_CurrentSubtree;
74 void AppendChar(char c);
76 template<class Iterator>
77 void AppendChars(Iterator begin, Iterator end);
79 void AppendJson(nlohmann::json json);
83 void FinishContainer(char terminator);
86 template<bool prettyPrint>
87 void Encode(JsonEncoder<prettyPrint>& stateMachine, const Value& value);
89 template<bool prettyPrint>
91 void EncodeNamespace(JsonEncoder<prettyPrint>& stateMachine, const Namespace::Ptr& ns)
93 stateMachine.StartObject();
96 for (const Namespace::Pair& kv : ns) {
97 stateMachine.Key(Utility::ValidateUTF8(kv.first));
98 Encode(stateMachine, kv.second->Get());
101 stateMachine.EndObject();
104 template<bool prettyPrint>
106 void EncodeDictionary(JsonEncoder<prettyPrint>& stateMachine, const Dictionary::Ptr& dict)
108 stateMachine.StartObject();
110 ObjectLock olock(dict);
111 for (const Dictionary::Pair& kv : dict) {
112 stateMachine.Key(Utility::ValidateUTF8(kv.first));
113 Encode(stateMachine, kv.second);
116 stateMachine.EndObject();
119 template<bool prettyPrint>
121 void EncodeArray(JsonEncoder<prettyPrint>& stateMachine, const Array::Ptr& arr)
123 stateMachine.StartArray();
125 ObjectLock olock(arr);
126 for (const Value& value : arr) {
127 Encode(stateMachine, value);
130 stateMachine.EndArray();
133 template<bool prettyPrint>
134 void Encode(JsonEncoder<prettyPrint>& stateMachine, const Value& value)
136 switch (value.GetType()) {
138 stateMachine.NumberFloat(value.Get<double>());
142 stateMachine.Boolean(value.ToBool());
146 stateMachine.Strng(Utility::ValidateUTF8(value.Get<String>()));
151 const Object::Ptr& obj = value.Get<Object::Ptr>();
154 Namespace::Ptr ns = dynamic_pointer_cast<Namespace>(obj);
156 EncodeNamespace(stateMachine, ns);
162 Dictionary::Ptr dict = dynamic_pointer_cast<Dictionary>(obj);
164 EncodeDictionary(stateMachine, dict);
169 Array::Ptr arr = dynamic_pointer_cast<Array>(obj);
171 EncodeArray(stateMachine, arr);
185 VERIFY(!"Invalid variant type.");
189 String icinga::JsonEncode(const Value& value, bool pretty_print)
192 JsonEncoder<true> stateMachine;
194 Encode(stateMachine, value);
196 return stateMachine.GetResult();
198 JsonEncoder<false> stateMachine;
200 Encode(stateMachine, value);
202 return stateMachine.GetResult();
206 Value icinga::JsonDecode(const String& data)
208 String sanitized (Utility::ValidateUTF8(data));
210 JsonSax stateMachine;
212 nlohmann::json::sax_parse(sanitized.Begin(), sanitized.End(), &stateMachine);
214 return stateMachine.GetResult();
220 FillCurrentTarget(Value());
226 bool JsonSax::boolean(bool val)
228 FillCurrentTarget(val);
234 bool JsonSax::number_integer(JsonSax::number_integer_t val)
236 FillCurrentTarget((double)val);
242 bool JsonSax::number_unsigned(JsonSax::number_unsigned_t val)
244 FillCurrentTarget((double)val);
250 bool JsonSax::number_float(JsonSax::number_float_t val, const JsonSax::string_t&)
252 FillCurrentTarget((double)val);
258 bool JsonSax::string(JsonSax::string_t& val)
260 FillCurrentTarget(String(std::move(val)));
266 bool JsonSax::start_object(std::size_t)
268 auto object (new Dictionary());
270 FillCurrentTarget(object);
272 m_CurrentSubtree.push({object, nullptr});
278 bool JsonSax::key(JsonSax::string_t& val)
280 m_CurrentKey = String(std::move(val));
286 bool JsonSax::end_object()
288 m_CurrentSubtree.pop();
289 m_CurrentKey = String();
295 bool JsonSax::start_array(std::size_t)
297 auto array (new Array());
299 FillCurrentTarget(array);
301 m_CurrentSubtree.push({nullptr, array});
307 bool JsonSax::end_array()
309 m_CurrentSubtree.pop();
315 bool JsonSax::parse_error(std::size_t, const std::string&, const nlohmann::detail::exception& ex)
317 throw std::invalid_argument(ex.what());
321 Value JsonSax::GetResult()
327 void JsonSax::FillCurrentTarget(Value value)
329 if (m_CurrentSubtree.empty()) {
332 auto& node (m_CurrentSubtree.top());
335 node.first->Set(m_CurrentKey, value);
337 node.second->Add(value);
342 template<bool prettyPrint>
344 void JsonEncoder<prettyPrint>::Null()
347 AppendChars((const char*)l_Null, (const char*)l_Null + 4);
350 template<bool prettyPrint>
352 void JsonEncoder<prettyPrint>::Boolean(bool value)
357 AppendChars((const char*)l_True, (const char*)l_True + 4);
359 AppendChars((const char*)l_False, (const char*)l_False + 5);
363 template<bool prettyPrint>
365 void JsonEncoder<prettyPrint>::NumberFloat(double value)
371 template<bool prettyPrint>
373 void JsonEncoder<prettyPrint>::Strng(String value)
376 AppendJson(std::move(value));
379 template<bool prettyPrint>
381 void JsonEncoder<prettyPrint>::StartObject()
386 m_CurrentSubtree.push(2);
389 template<bool prettyPrint>
391 void JsonEncoder<prettyPrint>::Key(String value)
393 m_CurrentKey = std::move(value);
396 template<bool prettyPrint>
398 void JsonEncoder<prettyPrint>::EndObject()
400 FinishContainer('}');
403 template<bool prettyPrint>
405 void JsonEncoder<prettyPrint>::StartArray()
410 m_CurrentSubtree.push(0);
413 template<bool prettyPrint>
415 void JsonEncoder<prettyPrint>::EndArray()
417 FinishContainer(']');
420 template<bool prettyPrint>
422 String JsonEncoder<prettyPrint>::GetResult()
424 return String(m_Result.begin(), m_Result.end());
427 template<bool prettyPrint>
429 void JsonEncoder<prettyPrint>::AppendChar(char c)
431 m_Result.emplace_back(c);
434 template<bool prettyPrint>
435 template<class Iterator>
437 void JsonEncoder<prettyPrint>::AppendChars(Iterator begin, Iterator end)
439 m_Result.insert(m_Result.end(), begin, end);
442 template<bool prettyPrint>
444 void JsonEncoder<prettyPrint>::AppendJson(nlohmann::json json)
446 nlohmann::detail::serializer<nlohmann::json>(nlohmann::detail::output_adapter<char>(m_Result), ' ').dump(std::move(json), prettyPrint, true, 0);
449 template<bool prettyPrint>
451 void JsonEncoder<prettyPrint>::BeforeItem()
453 if (!m_CurrentSubtree.empty()) {
454 auto& node (m_CurrentSubtree.top());
465 for (auto i (m_CurrentSubtree.size()); i; --i) {
466 AppendChars((const char*)l_Indent, (const char*)l_Indent + 4);
471 AppendJson(std::move(m_CurrentKey));
481 template<bool prettyPrint>
483 void JsonEncoder<prettyPrint>::FinishContainer(char terminator)
485 if (prettyPrint && m_CurrentSubtree.top()[0]) {
488 for (auto i (m_CurrentSubtree.size() - 1u); i; --i) {
489 AppendChars((const char*)l_Indent, (const char*)l_Indent + 4);
493 AppendChar(terminator);
495 m_CurrentSubtree.pop();