From 1b0367b7403150b79e7b663a75b4ef2459c457c4 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 14 Mar 2019 14:05:45 +0100 Subject: [PATCH] JsonDecode(): use nlohmann::json::sax_parse() --- lib/base/json.cpp | 303 +++++++++++++++++++--------------------------- 1 file changed, 123 insertions(+), 180 deletions(-) diff --git a/lib/base/json.cpp b/lib/base/json.cpp index 13cb143f8..b7da6d8bc 100644 --- a/lib/base/json.cpp +++ b/lib/base/json.cpp @@ -13,9 +13,47 @@ #include #include #include +#include using namespace icinga; +class JsonSax : public nlohmann::json_sax +{ +public: + bool null() override; + bool boolean(bool val) override; + bool number_integer(number_integer_t val) override; + bool number_unsigned(number_unsigned_t val) override; + bool number_float(number_float_t val, const string_t& s) override; + bool string(string_t& val) override; + bool start_object(std::size_t elements) override; + bool key(string_t& val) override; + bool end_object() override; + bool start_array(std::size_t elements) override; + bool end_array() override; + bool parse_error(std::size_t position, const std::string& last_token, const nlohmann::detail::exception& ex) override; + + Value GetResult(); + +private: + struct Node + { + union + { + Dictionary *Object; + Array *Aray; + }; + + bool IsObject; + }; + + Value m_Root; + std::stack m_CurrentSubtree; + String m_CurrentKey; + + void FillCurrentTarget(Value value); +}; + static void Encode(yajl_gen handle, const Value& value); #if YAJL_MAJOR < 2 @@ -140,231 +178,136 @@ String icinga::JsonEncode(const Value& value, bool pretty_print) return result; } -struct JsonElement -{ - String Key; - bool KeySet{false}; - Value EValue; -}; - -struct JsonContext +Value icinga::JsonDecode(const String& data) { -public: - void Push(const Value& value) - { - JsonElement element; - element.EValue = value; + JsonSax stateMachine; - m_Stack.push(element); - } + nlohmann::json::sax_parse(data.Begin(), data.End(), &stateMachine); - JsonElement Pop() - { - JsonElement value = m_Stack.top(); - m_Stack.pop(); - return value; - } - - void AddValue(const Value& value) - { - if (m_Stack.empty()) { - JsonElement element; - element.EValue = value; - m_Stack.push(element); - return; - } - - JsonElement& element = m_Stack.top(); - - if (element.EValue.IsObjectType()) { - if (!element.KeySet) { - element.Key = value; - element.KeySet = true; - } else { - Dictionary::Ptr dict = element.EValue; - dict->Set(element.Key, value); - element.KeySet = false; - } - } else if (element.EValue.IsObjectType()) { - Array::Ptr arr = element.EValue; - arr->Add(value); - } else { - BOOST_THROW_EXCEPTION(std::invalid_argument("Cannot add value to JSON element.")); - } - } + return stateMachine.GetResult(); +} - Value GetValue() const - { - ASSERT(m_Stack.size() == 1); - return m_Stack.top().EValue; - } +inline +bool JsonSax::null() +{ + FillCurrentTarget(Value()); - void SaveException() - { - m_Exception = boost::current_exception(); - } + return true; +} - void ThrowException() const - { - if (m_Exception) - boost::rethrow_exception(m_Exception); - } +inline +bool JsonSax::boolean(bool val) +{ + FillCurrentTarget(val); -private: - std::stack m_Stack; - Value m_Key; - boost::exception_ptr m_Exception; -}; + return true; +} -static int DecodeNull(void *ctx) +inline +bool JsonSax::number_integer(JsonSax::number_integer_t val) { - auto *context = static_cast(ctx); - - try { - context->AddValue(Empty); - } catch (...) { - context->SaveException(); - return 0; - } + FillCurrentTarget((double)val); - return 1; + return true; } -static int DecodeBoolean(void *ctx, int value) +inline +bool JsonSax::number_unsigned(JsonSax::number_unsigned_t val) { - auto *context = static_cast(ctx); + FillCurrentTarget((double)val); - try { - context->AddValue(static_cast(value)); - } catch (...) { - context->SaveException(); - return 0; - } - - return 1; + return true; } -static int DecodeNumber(void *ctx, const char *str, yajl_size len) +inline +bool JsonSax::number_float(JsonSax::number_float_t val, const JsonSax::string_t&) { - auto *context = static_cast(ctx); - - try { - String jstr = String(str, str + len); - context->AddValue(Convert::ToDouble(jstr)); - } catch (...) { - context->SaveException(); - return 0; - } + FillCurrentTarget((double)val); - return 1; + return true; } -static int DecodeString(void *ctx, const unsigned char *str, yajl_size len) +inline +bool JsonSax::string(JsonSax::string_t& val) { - auto *context = static_cast(ctx); - - try { - context->AddValue(String(str, str + len)); - } catch (...) { - context->SaveException(); - return 0; - } + FillCurrentTarget(String(std::move(val))); - return 1; + return true; } -static int DecodeStartMap(void *ctx) +inline +bool JsonSax::start_object(std::size_t) { - auto *context = static_cast(ctx); + auto object (new Dictionary()); - try { - context->Push(new Dictionary()); - } catch (...) { - context->SaveException(); - return 0; - } + FillCurrentTarget(object); + + m_CurrentSubtree.push(Node{{.Object = object}, true}); - return 1; + return true; } -static int DecodeEndMapOrArray(void *ctx) +inline +bool JsonSax::key(JsonSax::string_t& val) { - auto *context = static_cast(ctx); + m_CurrentKey = String(std::move(val)); - try { - context->AddValue(context->Pop().EValue); - } catch (...) { - context->SaveException(); - return 0; - } - - return 1; + return true; } -static int DecodeStartArray(void *ctx) +inline +bool JsonSax::end_object() { - auto *context = static_cast(ctx); + m_CurrentSubtree.pop(); + m_CurrentKey = String(); - try { - context->Push(new Array()); - } catch (...) { - context->SaveException(); - return 0; - } - - return 1; + return true; } -Value icinga::JsonDecode(const String& data) +inline +bool JsonSax::start_array(std::size_t) { - static const yajl_callbacks callbacks = { - DecodeNull, - DecodeBoolean, - nullptr, - nullptr, - DecodeNumber, - DecodeString, - DecodeStartMap, - DecodeString, - DecodeEndMapOrArray, - DecodeStartArray, - DecodeEndMapOrArray - }; + auto array (new Array()); - yajl_handle handle; -#if YAJL_MAJOR < 2 - yajl_parser_config cfg = { 1, 0 }; -#endif /* YAJL_MAJOR */ - JsonContext context; + FillCurrentTarget(array); -#if YAJL_MAJOR < 2 - handle = yajl_alloc(&callbacks, &cfg, nullptr, &context); -#else /* YAJL_MAJOR */ - handle = yajl_alloc(&callbacks, nullptr, &context); - yajl_config(handle, yajl_dont_validate_strings, 1); - yajl_config(handle, yajl_allow_comments, 1); -#endif /* YAJL_MAJOR */ + m_CurrentSubtree.push(Node{{.Aray = array}, false}); - yajl_parse(handle, reinterpret_cast(data.CStr()), data.GetLength()); + return true; +} -#if YAJL_MAJOR < 2 - if (yajl_parse_complete(handle) != yajl_status_ok) { -#else /* YAJL_MAJOR */ - if (yajl_complete_parse(handle) != yajl_status_ok) { -#endif /* YAJL_MAJOR */ - unsigned char *internal_err_str = yajl_get_error(handle, 1, reinterpret_cast(data.CStr()), data.GetLength()); - String msg = reinterpret_cast(internal_err_str); - yajl_free_error(handle, internal_err_str); +inline +bool JsonSax::end_array() +{ + m_CurrentSubtree.pop(); - yajl_free(handle); + return true; +} - /* throw saved exception (if there is one) */ - context.ThrowException(); +inline +bool JsonSax::parse_error(std::size_t, const std::string&, const nlohmann::detail::exception& ex) +{ + throw std::invalid_argument(ex.what()); +} - BOOST_THROW_EXCEPTION(std::invalid_argument(msg)); - } +inline +Value JsonSax::GetResult() +{ + return m_Root; +} - yajl_free(handle); +inline +void JsonSax::FillCurrentTarget(Value value) +{ + if (m_CurrentSubtree.empty()) { + m_Root = value; + } else { + auto& node (m_CurrentSubtree.top()); - return context.GetValue(); + if (node.IsObject) { + node.Object->Set(m_CurrentKey, value); + } else { + node.Aray->Add(value); + } + } } -- 2.40.0