#include <yajl/yajl_parse.h>
#include <json.hpp>
#include <stack>
+#include <utility>
using namespace icinga;
+class JsonSax : public nlohmann::json_sax<nlohmann::json>
+{
+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<Node> m_CurrentSubtree;
+ String m_CurrentKey;
+
+ void FillCurrentTarget(Value value);
+};
+
static void Encode(yajl_gen handle, const Value& value);
#if YAJL_MAJOR < 2
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<Dictionary>()) {
- 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>()) {
- 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<JsonElement> 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<JsonContext *>(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<JsonContext *>(ctx);
+ FillCurrentTarget((double)val);
- try {
- context->AddValue(static_cast<bool>(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<JsonContext *>(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<JsonContext *>(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<JsonContext *>(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<JsonContext *>(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<JsonContext *>(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<const unsigned char *>(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<const unsigned char *>(data.CStr()), data.GetLength());
- String msg = reinterpret_cast<char *>(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);
+ }
+ }
}