#ifndef JSON_B360EKF1
|
#define JSON_B360EKF1
|
|
#include <algorithm>
|
#include <boost/variant.hpp>
|
#include <map>
|
#include <string>
|
#include <vector>
|
|
namespace ssjson
|
{
|
|
class JValue
|
{
|
public:
|
typedef std::string key_type;
|
typedef long long int_type;
|
typedef long double float_type;
|
typedef JValue value_type;
|
typedef std::map<key_type, value_type> object_type;
|
typedef object_type::value_type pair_type;
|
typedef std::vector<value_type> array_type;
|
|
enum JVType { jv_null,
|
jv_int,
|
jv_float,
|
jv_bool,
|
jv_string,
|
jv_object,
|
jv_array };
|
std::string name() const
|
{
|
static const char *names[] = {"jv_null", "jv_int", "jv_float", "jv_bool", "jv_string", "jv_object", "jv_array"};
|
return names[type()];
|
}
|
|
private:
|
typedef boost::variant<std::nullptr_t, int_type, float_type, bool, std::string, object_type, array_type> data_type;
|
|
template <class C>
|
const C &val() const { return ref_val<C>(); }
|
template <class C>
|
C &val() { return const_cast<C &>(ref_val<C>()); }
|
|
template <class C>
|
class TInitValue
|
{
|
typedef C value_t;
|
typedef std::vector<TInitValue> list_t;
|
mutable boost::variant<value_t, list_t> data_;
|
|
public:
|
template <class T>
|
TInitValue(const T &t) :
|
data_(value_t(t)) {}
|
template <class T>
|
TInitValue(T &&t) :
|
data_(value_t(std::move(t))) {}
|
TInitValue(std::initializer_list<TInitValue> l) :
|
data_(list_t(l)){};
|
bool is_value() const { return data_.which() == 0; }
|
bool is_list() const { return data_.which() == 1; }
|
const list_t &list() const { return boost::get<list_t>(data_); }
|
const value_t &value() const { return boost::get<value_t>(data_); }
|
};
|
typedef TInitValue<JValue> InitValue;
|
typedef std::initializer_list<InitValue> InitList;
|
|
template <class Iter>
|
static object_type MakeObject(Iter begin, Iter end)
|
{
|
object_type obj;
|
for (auto it = begin; it != end; ++it) {
|
obj.emplace(std::move(it->list()[0].value().template val<key_type>()), (it->list()[1]));
|
}
|
return obj;
|
}
|
template <class Iter>
|
static array_type MakeArray(Iter begin, Iter end) { return array_type(begin, end); }
|
template <class Iter>
|
static JValue FromInit(Iter begin, Iter end)
|
{
|
auto like_object_item = [](const InitValue &v) {
|
return v.is_list() &&
|
v.list().size() == 2 &&
|
v.list()[0].is_value() &&
|
v.list()[0].value().type() == jv_string;
|
};
|
if (std::all_of(begin, end, like_object_item)) {
|
return MakeObject(begin, end);
|
} else {
|
return MakeArray(begin, end);
|
}
|
}
|
static JValue FromInit(const InitValue &ji)
|
{
|
if (ji.is_value()) {
|
return std::move(ji.value());
|
} else {
|
return FromInit(ji.list().begin(), ji.list().end());
|
}
|
}
|
|
public:
|
struct DumpOptions {
|
int indent_step_ = 0;
|
int indent_level_ = 0;
|
bool escape_unicode_ = false;
|
};
|
JValue() :
|
data_(object_type()) { static_assert(sizeof(JValue) == sizeof(data_type), "JValue should simple wrap data_type with no other fields."); }
|
#define SUPPORT_CONSTRUCTOR_AND_ASSIGNMENT(decl, expr) \
|
JValue(decl) : data_(expr) {} \
|
JValue &operator=(decl) \
|
{ \
|
data_ = (expr); \
|
return *this; \
|
}
|
// operator=() seems faster than JValue(exer).swap(data_).
|
// copy
|
SUPPORT_CONSTRUCTOR_AND_ASSIGNMENT(const JValue &v, v.data_) // self;
|
#define MAP_TYPE_TO_JSON_TYPE(decl, expr) \
|
public: \
|
SUPPORT_CONSTRUCTOR_AND_ASSIGNMENT(decl, expr) \
|
private: \
|
static auto JVTypeAccept(decl)->std::remove_reference<decltype(expr)>::type
|
#define MAP_TYPE_TO_JSON_TYPE_BIDIR(decl, expr) \
|
MAP_TYPE_TO_JSON_TYPE(decl, expr); \
|
\
|
private: \
|
static auto JVTypeReturn(decl)->std::remove_reference<decltype(expr)>::type
|
MAP_TYPE_TO_JSON_TYPE_BIDIR(const std::nullptr_t, nullptr);
|
MAP_TYPE_TO_JSON_TYPE_BIDIR(const short v, int_type(v));
|
MAP_TYPE_TO_JSON_TYPE_BIDIR(const unsigned short v, int_type(v));
|
MAP_TYPE_TO_JSON_TYPE_BIDIR(const int v, int_type(v));
|
MAP_TYPE_TO_JSON_TYPE_BIDIR(const unsigned int v, int_type(v));
|
MAP_TYPE_TO_JSON_TYPE_BIDIR(const long v, int_type(v));
|
MAP_TYPE_TO_JSON_TYPE_BIDIR(const unsigned long v, int_type(v));
|
MAP_TYPE_TO_JSON_TYPE_BIDIR(const long long v, int_type(v));
|
MAP_TYPE_TO_JSON_TYPE_BIDIR(const unsigned long long v, int_type(v));
|
MAP_TYPE_TO_JSON_TYPE_BIDIR(const float v, float_type(v));
|
MAP_TYPE_TO_JSON_TYPE_BIDIR(const double v, float_type(v));
|
MAP_TYPE_TO_JSON_TYPE_BIDIR(const long double v, float_type(v));
|
MAP_TYPE_TO_JSON_TYPE_BIDIR(const bool v, v);
|
MAP_TYPE_TO_JSON_TYPE_BIDIR(const std::string &v, v);
|
MAP_TYPE_TO_JSON_TYPE_BIDIR(const object_type &v, v);
|
MAP_TYPE_TO_JSON_TYPE_BIDIR(const array_type &v, v);
|
MAP_TYPE_TO_JSON_TYPE(const char *v, std::string(v));
|
#undef MAP_TYPE_TO_JSON_TYPE_BIDIR
|
#undef MAP_TYPE_TO_JSON_TYPE
|
template <class T>
|
static void JVTypeAccept(T); // use void to disable other types.
|
template <class T>
|
static void JVTypeReturn(T); // use void to disable other types.
|
public:
|
// move
|
SUPPORT_CONSTRUCTOR_AND_ASSIGNMENT(JValue &&v, std::move(v.data_)) // self
|
SUPPORT_CONSTRUCTOR_AND_ASSIGNMENT(std::string &&v, std::move(v)) // and other classes
|
SUPPORT_CONSTRUCTOR_AND_ASSIGNMENT(object_type &&v, std::move(v))
|
SUPPORT_CONSTRUCTOR_AND_ASSIGNMENT(array_type &&v, std::move(v))
|
#undef SUPPORT_CONSTRUCTOR_AND_ASSIGNMENT
|
|
static JValue Object()
|
{
|
return (object_type());
|
}
|
static JValue Object(InitList l) { return (MakeObject(l.begin(), l.end())); }
|
static JValue Array() { return (array_type()); }
|
static JValue Array(InitList l) { return (MakeArray(l.begin(), l.end())); }
|
JValue(InitList l) :
|
JValue(FromInit(l.begin(), l.end())) {}
|
JValue(const InitValue &ji) :
|
JValue(FromInit(ji)) {}
|
JVType type() const { return static_cast<JVType>(data_.which()); }
|
|
std::string dump(const DumpOptions &opt_in) const;
|
std::string dump(const int indent_step = 0, const int indent_level = 0, const bool escape_unicode = false) const
|
{
|
DumpOptions opt;
|
opt.indent_step_ = indent_step;
|
opt.indent_level_ = indent_level;
|
opt.escape_unicode_ = escape_unicode;
|
return dump(opt);
|
}
|
bool parse(const std::string &s) { return parse(s.c_str(), s.c_str() + s.size()) > 0; }
|
int parse(const char *begin, const char *end);
|
void swap(JValue &a) { data_.swap(a.data_); }
|
|
template <class T>
|
T get_value() const { return val<decltype(JVTypeReturn(T()))>(); }
|
template <class T>
|
void put_value(T &&v) { *this = v; }
|
const object_type &object() const { return val<object_type>(); }
|
object_type &object() { return val<object_type>(); }
|
const array_type &array() const { return val<array_type>(); }
|
array_type &array() { return val<array_type>(); }
|
bool is_object() const { return type() == jv_object; }
|
bool is_array() const { return type() == jv_array; }
|
|
// array ops
|
const value_type &operator[](const size_t idx) const { return array()[idx]; }
|
value_type &operator[](const size_t idx) { return array()[idx]; }
|
const value_type &at(const size_t idx) const { return array().at(idx); }
|
value_type &at(const size_t idx) { return array().at(idx); }
|
void push_back(const value_type &v) { array().push_back(v); }
|
void push_back(value_type &&v) { array().push_back(std::move(v)); }
|
|
// object ops
|
value_type &operator[](const key_type &key) { return object()[key]; }
|
const value_type &at(const key_type &key) const { return object().at(key); }
|
value_type &at(const key_type &key) { return object().at(key); }
|
const value_type &child(const key_type &path) const { return ref_child(path); }
|
value_type &child(const key_type &path) { return const_cast<value_type &>(ref_child(path)); }
|
template <class T>
|
T get(const key_type &path) const { return child(path).val<decltype(JVTypeReturn(T()))>(); }
|
template <class T>
|
auto get(const key_type &path, const T &def) const -> typename std::remove_reference<decltype(JVTypeAccept(def))>::type
|
{
|
try {
|
return get<decltype(JVTypeAccept(def))>(path);
|
} catch (...) {
|
return def;
|
}
|
}
|
value_type &put(pair_type &&elem)
|
{
|
const key_type &key = elem.first;
|
auto sep = key.find('.');
|
if (sep == key_type::npos) {
|
auto r = object().lower_bound(elem.first);
|
if (r != object().end() && r->first == elem.first) {
|
r->second = std::move(elem.second);
|
return r->second;
|
} else {
|
return object().emplace_hint(r, std::move(elem))->second;
|
}
|
} else {
|
return (*this)[key.substr(0, sep)].put(pair_type(key.substr(sep + 1), std::move(elem.second)));
|
}
|
}
|
value_type &put(const pair_type &elem)
|
{
|
pair_type tmp(elem);
|
return put(std::move(tmp));
|
}
|
value_type &put(const key_type &k, const value_type &v) { return put(pair_type(k, v)); }
|
value_type &put(const key_type &k, value_type &&v) { return put(pair_type(k, std::move(v))); }
|
value_type &put(key_type &&k, const value_type &v) { return put(pair_type(std::move(k), v)); }
|
value_type &put(key_type &&k, value_type &&v) { return put(pair_type(std::move(k), std::move(v))); }
|
|
private:
|
template <class C>
|
const C &ref_val() const
|
{
|
try {
|
return boost::get<C>(data_);
|
} catch (std::exception &) {
|
throw std::runtime_error("json get error, " + name() + " is not a " + JValue(C()).name());
|
}
|
}
|
const value_type &ref_child(const key_type &path) const
|
{
|
auto sep = path.find('.');
|
if (sep == key_type::npos) {
|
return at(path);
|
} else {
|
return at(path.substr(0, sep)).ref_child(path.substr(sep + 1));
|
}
|
}
|
data_type data_;
|
};
|
|
typedef JValue Json;
|
|
} // namespace ssjson
|
|
#endif // end of include guard: JSON_B360EKF1
|