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 -{ -public: - void Push(const Value& value) - { - JsonElement element; - element.EValue = value; - - m_Stack.push(element); - } - - 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.")); - } - } - - Value GetValue() const - { - ASSERT(m_Stack.size() == 1); - return m_Stack.top().EValue; - } - - void SaveException() - { - m_Exception = boost::current_exception(); - } - - void ThrowException() const - { - if (m_Exception) - boost::rethrow_exception(m_Exception); - } - -private: - std::stack m_Stack; - Value m_Key; - boost::exception_ptr m_Exception; -}; - -static int DecodeNull(void *ctx) -{ - auto *context = static_cast(ctx); - - try { - context->AddValue(Empty); - } catch (...) { - context->SaveException(); - return 0; - } - - return 1; -} - -static int DecodeBoolean(void *ctx, int value) -{ - auto *context = static_cast(ctx); - - try { - context->AddValue(static_cast(value)); - } catch (...) { - context->SaveException(); - return 0; - } - - return 1; -} - -static int DecodeNumber(void *ctx, const char *str, yajl_size len) -{ - auto *context = static_cast(ctx); - - try { - String jstr = String(str, str + len); - context->AddValue(Convert::ToDouble(jstr)); - } catch (...) { - context->SaveException(); - return 0; - } - - return 1; -} - -static int DecodeString(void *ctx, const unsigned char *str, yajl_size len) -{ - auto *context = static_cast(ctx); - - try { - context->AddValue(String(str, str + len)); - } catch (...) { - context->SaveException(); - return 0; - } - - return 1; -} - -static int DecodeStartMap(void *ctx) -{ - auto *context = static_cast(ctx); - - try { - context->Push(new Dictionary()); - } catch (...) { - context->SaveException(); - return 0; - } - - return 1; -} - -static int DecodeEndMapOrArray(void *ctx) -{ - auto *context = static_cast(ctx); - - try { - context->AddValue(context->Pop().EValue); - } catch (...) { - context->SaveException(); - return 0; - } - - return 1; -} - -static int DecodeStartArray(void *ctx) -{ - auto *context = static_cast(ctx); - - try { - context->Push(new Array()); - } catch (...) { - context->SaveException(); - return 0; - } - - return 1; -} - Value icinga::JsonDecode(const String& data) { - static const yajl_callbacks callbacks = { - DecodeNull, - DecodeBoolean, - nullptr, - nullptr, - DecodeNumber, - DecodeString, - DecodeStartMap, - DecodeString, - DecodeEndMapOrArray, - DecodeStartArray, - DecodeEndMapOrArray - }; + JsonSax stateMachine; - yajl_handle handle; -#if YAJL_MAJOR < 2 - yajl_parser_config cfg = { 1, 0 }; -#endif /* YAJL_MAJOR */ - JsonContext context; + nlohmann::json::sax_parse(data.Begin(), data.End(), &stateMachine); -#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 */ - - yajl_parse(handle, reinterpret_cast(data.CStr()), data.GetLength()); - -#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); - - yajl_free(handle); - - /* throw saved exception (if there is one) */ - context.ThrowException(); - - BOOST_THROW_EXCEPTION(std::invalid_argument(msg)); - } - - yajl_free(handle); - - return context.GetValue(); + return stateMachine.GetResult(); +} + +inline +bool JsonSax::null() +{ + FillCurrentTarget(Value()); + + return true; +} + +inline +bool JsonSax::boolean(bool val) +{ + FillCurrentTarget(val); + + return true; +} + +inline +bool JsonSax::number_integer(JsonSax::number_integer_t val) +{ + FillCurrentTarget((double)val); + + return true; +} + +inline +bool JsonSax::number_unsigned(JsonSax::number_unsigned_t val) +{ + FillCurrentTarget((double)val); + + return true; +} + +inline +bool JsonSax::number_float(JsonSax::number_float_t val, const JsonSax::string_t&) +{ + FillCurrentTarget((double)val); + + return true; +} + +inline +bool JsonSax::string(JsonSax::string_t& val) +{ + FillCurrentTarget(String(std::move(val))); + + return true; +} + +inline +bool JsonSax::start_object(std::size_t) +{ + auto object (new Dictionary()); + + FillCurrentTarget(object); + + m_CurrentSubtree.push(Node{{.Object = object}, true}); + + return true; +} + +inline +bool JsonSax::key(JsonSax::string_t& val) +{ + m_CurrentKey = String(std::move(val)); + + return true; +} + +inline +bool JsonSax::end_object() +{ + m_CurrentSubtree.pop(); + m_CurrentKey = String(); + + return true; +} + +inline +bool JsonSax::start_array(std::size_t) +{ + auto array (new Array()); + + FillCurrentTarget(array); + + m_CurrentSubtree.push(Node{{.Aray = array}, false}); + + return true; +} + +inline +bool JsonSax::end_array() +{ + m_CurrentSubtree.pop(); + + return true; +} + +inline +bool JsonSax::parse_error(std::size_t, const std::string&, const nlohmann::detail::exception& ex) +{ + throw std::invalid_argument(ex.what()); +} + +inline +Value JsonSax::GetResult() +{ + return m_Root; +} + +inline +void JsonSax::FillCurrentTarget(Value value) +{ + if (m_CurrentSubtree.empty()) { + m_Root = value; + } else { + auto& node (m_CurrentSubtree.top()); + + if (node.IsObject) { + node.Object->Set(m_CurrentKey, value); + } else { + node.Aray->Add(value); + } + } }