2019-02-25 14:48:22 +01:00
|
|
|
/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
|
2014-10-26 19:59:49 +01:00
|
|
|
|
|
|
|
#include "base/json.hpp"
|
|
|
|
#include "base/debug.hpp"
|
2018-08-07 13:55:41 +02:00
|
|
|
#include "base/namespace.hpp"
|
2014-10-26 19:59:49 +01:00
|
|
|
#include "base/dictionary.hpp"
|
|
|
|
#include "base/array.hpp"
|
|
|
|
#include "base/objectlock.hpp"
|
|
|
|
#include "base/convert.hpp"
|
2016-08-31 13:43:14 +02:00
|
|
|
#include <boost/exception_ptr.hpp>
|
2014-10-28 05:55:08 +01:00
|
|
|
#include <yajl/yajl_version.h>
|
2014-10-26 19:59:49 +01:00
|
|
|
#include <yajl/yajl_gen.h>
|
|
|
|
#include <yajl/yajl_parse.h>
|
2018-10-18 15:22:50 +02:00
|
|
|
#include <json.hpp>
|
2014-10-26 19:59:49 +01:00
|
|
|
#include <stack>
|
2019-03-14 14:05:45 +01:00
|
|
|
#include <utility>
|
2014-10-26 19:59:49 +01:00
|
|
|
|
|
|
|
using namespace icinga;
|
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
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);
|
|
|
|
};
|
|
|
|
|
2014-10-26 19:59:49 +01:00
|
|
|
static void Encode(yajl_gen handle, const Value& value);
|
|
|
|
|
2014-10-28 08:12:54 +01:00
|
|
|
#if YAJL_MAJOR < 2
|
2014-10-28 06:33:40 +01:00
|
|
|
typedef unsigned int yajl_size;
|
|
|
|
#else /* YAJL_MAJOR */
|
|
|
|
typedef size_t yajl_size;
|
|
|
|
#endif /* YAJL_MAJOR */
|
|
|
|
|
2018-08-07 13:55:41 +02:00
|
|
|
static void EncodeNamespace(yajl_gen handle, const Namespace::Ptr& ns)
|
|
|
|
{
|
|
|
|
yajl_gen_map_open(handle);
|
|
|
|
|
|
|
|
ObjectLock olock(ns);
|
|
|
|
for (const Namespace::Pair& kv : ns) {
|
|
|
|
yajl_gen_string(handle, reinterpret_cast<const unsigned char *>(kv.first.CStr()), kv.first.GetLength());
|
|
|
|
Encode(handle, kv.second->Get());
|
|
|
|
}
|
|
|
|
|
|
|
|
yajl_gen_map_close(handle);
|
|
|
|
}
|
|
|
|
|
2014-10-26 19:59:49 +01:00
|
|
|
static void EncodeDictionary(yajl_gen handle, const Dictionary::Ptr& dict)
|
|
|
|
{
|
|
|
|
yajl_gen_map_open(handle);
|
|
|
|
|
|
|
|
ObjectLock olock(dict);
|
2016-08-25 06:19:44 +02:00
|
|
|
for (const Dictionary::Pair& kv : dict) {
|
2014-10-26 19:59:49 +01:00
|
|
|
yajl_gen_string(handle, reinterpret_cast<const unsigned char *>(kv.first.CStr()), kv.first.GetLength());
|
|
|
|
Encode(handle, kv.second);
|
|
|
|
}
|
|
|
|
|
|
|
|
yajl_gen_map_close(handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void EncodeArray(yajl_gen handle, const Array::Ptr& arr)
|
|
|
|
{
|
|
|
|
yajl_gen_array_open(handle);
|
|
|
|
|
|
|
|
ObjectLock olock(arr);
|
2016-08-25 06:19:44 +02:00
|
|
|
for (const Value& value : arr) {
|
2014-10-26 19:59:49 +01:00
|
|
|
Encode(handle, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
yajl_gen_array_close(handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void Encode(yajl_gen handle, const Value& value)
|
|
|
|
{
|
|
|
|
switch (value.GetType()) {
|
|
|
|
case ValueNumber:
|
2016-08-27 12:20:54 +02:00
|
|
|
if (yajl_gen_double(handle, value.Get<double>()) == yajl_gen_invalid_number)
|
2014-10-26 19:59:49 +01:00
|
|
|
yajl_gen_double(handle, 0);
|
|
|
|
|
2014-12-10 09:04:49 +01:00
|
|
|
break;
|
|
|
|
case ValueBoolean:
|
|
|
|
yajl_gen_bool(handle, value.ToBool());
|
|
|
|
|
2014-10-26 19:59:49 +01:00
|
|
|
break;
|
|
|
|
case ValueString:
|
2016-08-27 11:47:36 +02:00
|
|
|
yajl_gen_string(handle, reinterpret_cast<const unsigned char *>(value.Get<String>().CStr()), value.Get<String>().GetLength());
|
2014-10-26 19:59:49 +01:00
|
|
|
|
|
|
|
break;
|
|
|
|
case ValueObject:
|
2018-01-11 07:29:34 +01:00
|
|
|
{
|
|
|
|
const Object::Ptr& obj = value.Get<Object::Ptr>();
|
2018-08-07 13:55:41 +02:00
|
|
|
Namespace::Ptr ns = dynamic_pointer_cast<Namespace>(obj);
|
|
|
|
|
|
|
|
if (ns) {
|
|
|
|
EncodeNamespace(handle, ns);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-01-11 07:29:34 +01:00
|
|
|
Dictionary::Ptr dict = dynamic_pointer_cast<Dictionary>(obj);
|
|
|
|
|
|
|
|
if (dict) {
|
|
|
|
EncodeDictionary(handle, dict);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
Array::Ptr arr = dynamic_pointer_cast<Array>(obj);
|
|
|
|
|
|
|
|
if (arr) {
|
|
|
|
EncodeArray(handle, arr);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
yajl_gen_null(handle);
|
2014-10-26 19:59:49 +01:00
|
|
|
|
|
|
|
break;
|
|
|
|
case ValueEmpty:
|
|
|
|
yajl_gen_null(handle);
|
|
|
|
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
VERIFY(!"Invalid variant type.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-16 19:28:46 +01:00
|
|
|
String icinga::JsonEncode(const Value& value, bool pretty_print)
|
2014-10-26 19:59:49 +01:00
|
|
|
{
|
2014-10-28 05:55:08 +01:00
|
|
|
#if YAJL_MAJOR < 2
|
2014-12-16 19:28:46 +01:00
|
|
|
yajl_gen_config conf = { pretty_print, "" };
|
2017-12-14 15:37:20 +01:00
|
|
|
yajl_gen handle = yajl_gen_alloc(&conf, nullptr);
|
2014-10-28 05:55:08 +01:00
|
|
|
#else /* YAJL_MAJOR */
|
2017-12-14 15:37:20 +01:00
|
|
|
yajl_gen handle = yajl_gen_alloc(nullptr);
|
2014-12-16 19:28:46 +01:00
|
|
|
if (pretty_print)
|
|
|
|
yajl_gen_config(handle, yajl_gen_beautify, 1);
|
2014-10-28 05:55:08 +01:00
|
|
|
#endif /* YAJL_MAJOR */
|
|
|
|
|
2014-10-26 19:59:49 +01:00
|
|
|
Encode(handle, value);
|
|
|
|
|
|
|
|
const unsigned char *buf;
|
2014-10-28 06:33:40 +01:00
|
|
|
yajl_size len;
|
|
|
|
|
2014-10-26 19:59:49 +01:00
|
|
|
yajl_gen_get_buf(handle, &buf, &len);
|
|
|
|
|
|
|
|
String result = String(buf, buf + len);
|
|
|
|
|
|
|
|
yajl_gen_free(handle);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
Value icinga::JsonDecode(const String& data)
|
2014-10-26 19:59:49 +01:00
|
|
|
{
|
2019-03-14 14:05:45 +01:00
|
|
|
JsonSax stateMachine;
|
2014-10-26 19:59:49 +01:00
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
nlohmann::json::sax_parse(data.Begin(), data.End(), &stateMachine);
|
2014-10-26 19:59:49 +01:00
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
return stateMachine.GetResult();
|
|
|
|
}
|
2014-10-26 19:59:49 +01:00
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
inline
|
|
|
|
bool JsonSax::null()
|
|
|
|
{
|
|
|
|
FillCurrentTarget(Value());
|
2014-10-26 19:59:49 +01:00
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
return true;
|
|
|
|
}
|
2014-10-26 19:59:49 +01:00
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
inline
|
|
|
|
bool JsonSax::boolean(bool val)
|
|
|
|
{
|
|
|
|
FillCurrentTarget(val);
|
2014-10-26 19:59:49 +01:00
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
return true;
|
|
|
|
}
|
2014-10-26 19:59:49 +01:00
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
inline
|
|
|
|
bool JsonSax::number_integer(JsonSax::number_integer_t val)
|
2014-10-26 19:59:49 +01:00
|
|
|
{
|
2019-03-14 14:05:45 +01:00
|
|
|
FillCurrentTarget((double)val);
|
2014-10-26 19:59:49 +01:00
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
return true;
|
2014-10-26 19:59:49 +01:00
|
|
|
}
|
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
inline
|
|
|
|
bool JsonSax::number_unsigned(JsonSax::number_unsigned_t val)
|
2014-10-26 19:59:49 +01:00
|
|
|
{
|
2019-03-14 14:05:45 +01:00
|
|
|
FillCurrentTarget((double)val);
|
2014-10-26 19:59:49 +01:00
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
return true;
|
2014-10-26 19:59:49 +01:00
|
|
|
}
|
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
inline
|
|
|
|
bool JsonSax::number_float(JsonSax::number_float_t val, const JsonSax::string_t&)
|
2014-10-26 19:59:49 +01:00
|
|
|
{
|
2019-03-14 14:05:45 +01:00
|
|
|
FillCurrentTarget((double)val);
|
2014-10-26 19:59:49 +01:00
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
return true;
|
2014-10-26 19:59:49 +01:00
|
|
|
}
|
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
inline
|
|
|
|
bool JsonSax::string(JsonSax::string_t& val)
|
2014-10-26 19:59:49 +01:00
|
|
|
{
|
2019-03-14 14:05:45 +01:00
|
|
|
FillCurrentTarget(String(std::move(val)));
|
2014-10-26 19:59:49 +01:00
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
return true;
|
2014-10-26 19:59:49 +01:00
|
|
|
}
|
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
inline
|
|
|
|
bool JsonSax::start_object(std::size_t)
|
2014-10-26 19:59:49 +01:00
|
|
|
{
|
2019-03-14 14:05:45 +01:00
|
|
|
auto object (new Dictionary());
|
2014-10-26 19:59:49 +01:00
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
FillCurrentTarget(object);
|
|
|
|
|
|
|
|
m_CurrentSubtree.push(Node{{.Object = object}, true});
|
2014-10-26 19:59:49 +01:00
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
return true;
|
2014-10-26 19:59:49 +01:00
|
|
|
}
|
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
inline
|
|
|
|
bool JsonSax::key(JsonSax::string_t& val)
|
2014-10-26 19:59:49 +01:00
|
|
|
{
|
2019-03-14 14:05:45 +01:00
|
|
|
m_CurrentKey = String(std::move(val));
|
2014-10-26 19:59:49 +01:00
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
return true;
|
2014-10-26 19:59:49 +01:00
|
|
|
}
|
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
inline
|
|
|
|
bool JsonSax::end_object()
|
2014-10-26 19:59:49 +01:00
|
|
|
{
|
2019-03-14 14:05:45 +01:00
|
|
|
m_CurrentSubtree.pop();
|
|
|
|
m_CurrentKey = String();
|
2017-12-13 12:54:14 +01:00
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
return true;
|
2014-10-26 19:59:49 +01:00
|
|
|
}
|
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
inline
|
|
|
|
bool JsonSax::start_array(std::size_t)
|
2014-10-26 19:59:49 +01:00
|
|
|
{
|
2019-03-14 14:05:45 +01:00
|
|
|
auto array (new Array());
|
2014-10-26 19:59:49 +01:00
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
FillCurrentTarget(array);
|
2014-10-26 19:59:49 +01:00
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
m_CurrentSubtree.push(Node{{.Aray = array}, false});
|
2014-10-26 19:59:49 +01:00
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
return true;
|
|
|
|
}
|
2014-10-26 19:59:49 +01:00
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
inline
|
|
|
|
bool JsonSax::end_array()
|
|
|
|
{
|
|
|
|
m_CurrentSubtree.pop();
|
2014-10-26 19:59:49 +01:00
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
return true;
|
|
|
|
}
|
2014-10-26 19:59:49 +01:00
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
inline
|
|
|
|
bool JsonSax::parse_error(std::size_t, const std::string&, const nlohmann::detail::exception& ex)
|
|
|
|
{
|
|
|
|
throw std::invalid_argument(ex.what());
|
|
|
|
}
|
2014-10-27 09:28:32 +01:00
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
inline
|
|
|
|
Value JsonSax::GetResult()
|
|
|
|
{
|
|
|
|
return m_Root;
|
|
|
|
}
|
2014-10-26 19:59:49 +01:00
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
inline
|
|
|
|
void JsonSax::FillCurrentTarget(Value value)
|
|
|
|
{
|
|
|
|
if (m_CurrentSubtree.empty()) {
|
|
|
|
m_Root = value;
|
|
|
|
} else {
|
|
|
|
auto& node (m_CurrentSubtree.top());
|
2014-10-26 19:59:49 +01:00
|
|
|
|
2019-03-14 14:05:45 +01:00
|
|
|
if (node.IsObject) {
|
|
|
|
node.Object->Set(m_CurrentKey, value);
|
|
|
|
} else {
|
|
|
|
node.Aray->Add(value);
|
|
|
|
}
|
|
|
|
}
|
2014-10-26 19:59:49 +01:00
|
|
|
}
|