From 5882594b43720806acac7216298901e0a7d90f6b Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 15 Mar 2019 09:16:41 +0100 Subject: [PATCH] JsonEncode(): use nlohmann::json --- lib/base/json.cpp | 314 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 254 insertions(+), 60 deletions(-) diff --git a/lib/base/json.cpp b/lib/base/json.cpp index b7da6d8bc..286dc9ea8 100644 --- a/lib/base/json.cpp +++ b/lib/base/json.cpp @@ -7,13 +7,16 @@ #include "base/array.hpp" #include "base/objectlock.hpp" #include "base/convert.hpp" +#include #include +#include #include #include #include #include #include #include +#include using namespace icinga; @@ -54,100 +57,143 @@ private: void FillCurrentTarget(Value value); }; -static void Encode(yajl_gen handle, const Value& value); +const char l_Null[] = "null"; +const char l_False[] = "false"; +const char l_True[] = "true"; +const char l_Indent[] = " "; -#if YAJL_MAJOR < 2 -typedef unsigned int yajl_size; -#else /* YAJL_MAJOR */ -typedef size_t yajl_size; -#endif /* YAJL_MAJOR */ - -static void EncodeNamespace(yajl_gen handle, const Namespace::Ptr& ns) +// https://github.com/nlohmann/json/issues/1512 +template +class JsonEncoder { - yajl_gen_map_open(handle); +public: + void Null(); + void Boolean(bool value); + void NumberFloat(double value); + void Strng(String value); + void StartObject(); + void Key(String value); + void EndObject(); + void StartArray(); + void EndArray(); + + String GetResult(); + +private: + std::vector m_Result; + String m_CurrentKey; + std::stack> m_CurrentSubtree; + + void AppendChar(char c); + + template + void AppendChars(Iterator begin, Iterator end); + + void AppendJson(nlohmann::json json); + + void BeforeItem(); + + void FinishContainer(char terminator); +}; + +template +void Encode(JsonEncoder& stateMachine, const Value& value); + +template +inline +void EncodeNamespace(JsonEncoder& stateMachine, const Namespace::Ptr& ns) +{ + stateMachine.StartObject(); ObjectLock olock(ns); for (const Namespace::Pair& kv : ns) { - yajl_gen_string(handle, reinterpret_cast(kv.first.CStr()), kv.first.GetLength()); - Encode(handle, kv.second->Get()); + stateMachine.Key(kv.first); + Encode(stateMachine, kv.second->Get()); } - yajl_gen_map_close(handle); + stateMachine.EndObject(); } -static void EncodeDictionary(yajl_gen handle, const Dictionary::Ptr& dict) +template +inline +void EncodeDictionary(JsonEncoder& stateMachine, const Dictionary::Ptr& dict) { - yajl_gen_map_open(handle); + stateMachine.StartObject(); ObjectLock olock(dict); for (const Dictionary::Pair& kv : dict) { - yajl_gen_string(handle, reinterpret_cast(kv.first.CStr()), kv.first.GetLength()); - Encode(handle, kv.second); + stateMachine.Key(kv.first); + Encode(stateMachine, kv.second); } - yajl_gen_map_close(handle); + stateMachine.EndObject(); } -static void EncodeArray(yajl_gen handle, const Array::Ptr& arr) +template +inline +void EncodeArray(JsonEncoder& stateMachine, const Array::Ptr& arr) { - yajl_gen_array_open(handle); + stateMachine.StartArray(); ObjectLock olock(arr); for (const Value& value : arr) { - Encode(handle, value); + Encode(stateMachine, value); } - yajl_gen_array_close(handle); + stateMachine.EndArray(); } -static void Encode(yajl_gen handle, const Value& value) +template +void Encode(JsonEncoder& stateMachine, const Value& value) { switch (value.GetType()) { case ValueNumber: - if (yajl_gen_double(handle, value.Get()) == yajl_gen_invalid_number) - yajl_gen_double(handle, 0); - + stateMachine.NumberFloat(value.Get()); break; + case ValueBoolean: - yajl_gen_bool(handle, value.ToBool()); - + stateMachine.Boolean(value.ToBool()); break; + case ValueString: - yajl_gen_string(handle, reinterpret_cast(value.Get().CStr()), value.Get().GetLength()); - + stateMachine.Strng(value.Get()); break; + case ValueObject: { const Object::Ptr& obj = value.Get(); - Namespace::Ptr ns = dynamic_pointer_cast(obj); - if (ns) { - EncodeNamespace(handle, ns); - break; + { + Namespace::Ptr ns = dynamic_pointer_cast(obj); + if (ns) { + EncodeNamespace(stateMachine, ns); + break; + } } - Dictionary::Ptr dict = dynamic_pointer_cast(obj); - - if (dict) { - EncodeDictionary(handle, dict); - break; + { + Dictionary::Ptr dict = dynamic_pointer_cast(obj); + if (dict) { + EncodeDictionary(stateMachine, dict); + break; + } } Array::Ptr arr = dynamic_pointer_cast(obj); - if (arr) { - EncodeArray(handle, arr); + EncodeArray(stateMachine, arr); break; } } - yajl_gen_null(handle); + stateMachine.Null(); break; + case ValueEmpty: - yajl_gen_null(handle); - + stateMachine.Null(); break; + default: VERIFY(!"Invalid variant type."); } @@ -155,27 +201,19 @@ static void Encode(yajl_gen handle, const Value& value) String icinga::JsonEncode(const Value& value, bool pretty_print) { -#if YAJL_MAJOR < 2 - yajl_gen_config conf = { pretty_print, "" }; - yajl_gen handle = yajl_gen_alloc(&conf, nullptr); -#else /* YAJL_MAJOR */ - yajl_gen handle = yajl_gen_alloc(nullptr); - if (pretty_print) - yajl_gen_config(handle, yajl_gen_beautify, 1); -#endif /* YAJL_MAJOR */ + if (pretty_print) { + JsonEncoder stateMachine; - Encode(handle, value); + Encode(stateMachine, value); - const unsigned char *buf; - yajl_size len; + return stateMachine.GetResult(); + } else { + JsonEncoder stateMachine; - yajl_gen_get_buf(handle, &buf, &len); + Encode(stateMachine, value); - String result = String(buf, buf + len); - - yajl_gen_free(handle); - - return result; + return stateMachine.GetResult(); + } } Value icinga::JsonDecode(const String& data) @@ -311,3 +349,159 @@ void JsonSax::FillCurrentTarget(Value value) } } } + +template +inline +void JsonEncoder::Null() +{ + BeforeItem(); + AppendChars((const char*)l_Null, (const char*)l_Null + 4); +} + +template +inline +void JsonEncoder::Boolean(bool value) +{ + BeforeItem(); + + if (value) { + AppendChars((const char*)l_True, (const char*)l_True + 4); + } else { + AppendChars((const char*)l_False, (const char*)l_False + 5); + } +} + +template +inline +void JsonEncoder::NumberFloat(double value) +{ + BeforeItem(); + AppendJson(value); +} + +template +inline +void JsonEncoder::Strng(String value) +{ + BeforeItem(); + AppendJson(std::move(value)); +} + +template +inline +void JsonEncoder::StartObject() +{ + BeforeItem(); + AppendChar('{'); + + m_CurrentSubtree.push(2); +} + +template +inline +void JsonEncoder::Key(String value) +{ + m_CurrentKey = std::move(value); +} + +template +inline +void JsonEncoder::EndObject() +{ + FinishContainer('}'); +} + +template +inline +void JsonEncoder::StartArray() +{ + BeforeItem(); + AppendChar('['); + + m_CurrentSubtree.push(0); +} + +template +inline +void JsonEncoder::EndArray() +{ + FinishContainer(']'); +} + +template +inline +String JsonEncoder::GetResult() +{ + return String(m_Result.begin(), m_Result.end()); +} + +template +inline +void JsonEncoder::AppendChar(char c) +{ + m_Result.emplace_back(c); +} + +template +template +inline +void JsonEncoder::AppendChars(Iterator begin, Iterator end) +{ + m_Result.insert(m_Result.end(), begin, end); +} + +template +inline +void JsonEncoder::AppendJson(nlohmann::json json) +{ + nlohmann::detail::serializer(nlohmann::detail::output_adapter(m_Result), ' ').dump(std::move(json), prettyPrint, true, 0); +} + +template +inline +void JsonEncoder::BeforeItem() +{ + if (!m_CurrentSubtree.empty()) { + auto& node (m_CurrentSubtree.top()); + + if (node[0]) { + AppendChar(','); + } else { + node[0] = true; + } + + if (prettyPrint) { + AppendChar('\n'); + + for (auto i (m_CurrentSubtree.size()); i; --i) { + AppendChars((const char*)l_Indent, (const char*)l_Indent + 4); + } + } + + if (node[1]) { + AppendJson(std::move(m_CurrentKey)); + AppendChar(':'); + + if (prettyPrint) { + AppendChar(' '); + } + } + } +} + +template +inline +void JsonEncoder::FinishContainer(char terminator) +{ + if (prettyPrint && m_CurrentSubtree.top()[0]) { + AppendChar('\n'); + + for (auto i (m_CurrentSubtree.size() - 1u); i; --i) { + AppendChars((const char*)l_Indent, (const char*)l_Indent + 4); + } + } + + AppendChar(terminator); + + m_CurrentSubtree.pop(); +}