2286 lines
64 KiB
C++
2286 lines
64 KiB
C++
|
#ifndef _NLOHMANN_JSON
|
||
|
#define _NLOHMANN_JSON
|
||
|
|
||
|
#include <algorithm>
|
||
|
#include <cassert>
|
||
|
#include <functional>
|
||
|
#include <initializer_list>
|
||
|
#include <iostream>
|
||
|
#include <iterator>
|
||
|
#include <limits>
|
||
|
#include <map>
|
||
|
#include <memory>
|
||
|
#include <string>
|
||
|
#include <type_traits>
|
||
|
#include <utility>
|
||
|
#include <vector>
|
||
|
|
||
|
/*!
|
||
|
- ObjectType trick from http://stackoverflow.com/a/9860911
|
||
|
*/
|
||
|
/*
|
||
|
template<typename C, typename=void>
|
||
|
struct container_resizable : std::false_type {};
|
||
|
template<typename C>
|
||
|
struct container_resizable<C, decltype(C().resize(0))> : std::true_type {};
|
||
|
*/
|
||
|
|
||
|
/*!
|
||
|
@see https://github.com/nlohmann
|
||
|
*/
|
||
|
namespace nlohmann
|
||
|
{
|
||
|
|
||
|
/*!
|
||
|
@brief JSON
|
||
|
|
||
|
@tparam ObjectType type for JSON objects
|
||
|
(@c std::map by default)
|
||
|
@tparam ArrayType type for JSON arrays
|
||
|
(@c std::vector by default)
|
||
|
@tparam StringType type for JSON strings and object keys
|
||
|
(@c std::string by default)
|
||
|
@tparam BooleanType type for JSON booleans
|
||
|
(@c bool by default)
|
||
|
@tparam NumberIntegerType type for JSON integer numbers
|
||
|
(@c int64_t by default)
|
||
|
@tparam NumberFloatType type for JSON floating point numbers
|
||
|
(@c double by default)
|
||
|
*/
|
||
|
template <
|
||
|
template<typename U, typename V, typename... Args> class ObjectType = std::map,
|
||
|
//template<typename... Args> class ArrayType = std::vector,
|
||
|
template<typename U, typename... Args> class ArrayType = std::vector,
|
||
|
class StringType = std::string,
|
||
|
class BooleanType = bool,
|
||
|
class NumberIntegerType = int64_t,
|
||
|
class NumberFloatType = double
|
||
|
>
|
||
|
class basic_json
|
||
|
{
|
||
|
public:
|
||
|
/////////////////////
|
||
|
// container types //
|
||
|
/////////////////////
|
||
|
|
||
|
class iterator;
|
||
|
class const_iterator;
|
||
|
|
||
|
/// the type of elements in a basic_json container
|
||
|
using value_type = basic_json;
|
||
|
/// the type of an element reference
|
||
|
using reference = basic_json&;
|
||
|
/// the type of an element const reference
|
||
|
using const_reference = const basic_json&;
|
||
|
/// the type of an element pointer
|
||
|
using pointer = basic_json*;
|
||
|
/// the type of an element const pointer
|
||
|
using const_pointer = const basic_json*;
|
||
|
/// a type to represent differences between iterators
|
||
|
using difference_type = std::ptrdiff_t;
|
||
|
/// a type to represent container sizes
|
||
|
using size_type = std::size_t;
|
||
|
/// an iterator for a basic_json container
|
||
|
using iterator = basic_json::iterator;
|
||
|
/// a const iterator for a basic_json container
|
||
|
using const_iterator = basic_json::const_iterator;
|
||
|
|
||
|
|
||
|
///////////////////////////
|
||
|
// JSON value data types //
|
||
|
///////////////////////////
|
||
|
|
||
|
/// a type for an object
|
||
|
using object_t = ObjectType<StringType, basic_json>;
|
||
|
/// a type for an array
|
||
|
using array_t = ArrayType<basic_json>;
|
||
|
/// a type for a string
|
||
|
using string_t = StringType;
|
||
|
/// a type for a boolean
|
||
|
using boolean_t = BooleanType;
|
||
|
/// a type for a number (integer)
|
||
|
using number_integer_t = NumberIntegerType;
|
||
|
/// a type for a number (floating point)
|
||
|
using number_float_t = NumberFloatType;
|
||
|
/// a type for list initialization
|
||
|
using list_init_t = std::initializer_list<basic_json>;
|
||
|
|
||
|
|
||
|
////////////////////////
|
||
|
// JSON value storage //
|
||
|
////////////////////////
|
||
|
|
||
|
/// a JSON value
|
||
|
union json_value
|
||
|
{
|
||
|
/// object (stored with pointer to save storage)
|
||
|
object_t* object;
|
||
|
/// array (stored with pointer to save storage)
|
||
|
array_t* array;
|
||
|
/// string (stored with pointer to save storage)
|
||
|
string_t* string;
|
||
|
/// bolean
|
||
|
boolean_t boolean;
|
||
|
/// number (integer)
|
||
|
number_integer_t number_integer;
|
||
|
/// number (floating point)
|
||
|
number_float_t number_float;
|
||
|
|
||
|
/// default constructor (for null values)
|
||
|
json_value() = default;
|
||
|
/// constructor for objects
|
||
|
json_value(object_t* v) : object(v) {}
|
||
|
/// constructor for arrays
|
||
|
json_value(array_t* v) : array(v) {}
|
||
|
/// constructor for strings
|
||
|
json_value(string_t* v) : string(v) {}
|
||
|
/// constructor for booleans
|
||
|
json_value(boolean_t v) : boolean(v) {}
|
||
|
/// constructor for numbers (integer)
|
||
|
json_value(number_integer_t v) : number_integer(v) {}
|
||
|
/// constructor for numbers (floating point)
|
||
|
json_value(number_float_t v) : number_float(v) {}
|
||
|
};
|
||
|
|
||
|
|
||
|
/////////////////////////////////
|
||
|
// JSON value type enumeration //
|
||
|
/////////////////////////////////
|
||
|
|
||
|
/// JSON value type enumeration
|
||
|
enum class value_t : uint8_t
|
||
|
{
|
||
|
/// null value
|
||
|
null,
|
||
|
/// object (unordered set of name/value pairs)
|
||
|
object,
|
||
|
/// array (ordered collection of values)
|
||
|
array,
|
||
|
/// string value
|
||
|
string,
|
||
|
/// boolean value
|
||
|
boolean,
|
||
|
/// number value (integer)
|
||
|
number_integer,
|
||
|
/// number value (floating point)
|
||
|
number_float
|
||
|
};
|
||
|
|
||
|
|
||
|
//////////////////
|
||
|
// constructors //
|
||
|
//////////////////
|
||
|
|
||
|
/// create an empty value with a given type
|
||
|
inline basic_json(const value_t value)
|
||
|
: m_type(value)
|
||
|
{
|
||
|
switch (m_type)
|
||
|
{
|
||
|
case (value_t::null):
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case (value_t::object):
|
||
|
{
|
||
|
m_value.object = new object_t();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case (value_t::array):
|
||
|
{
|
||
|
m_value.array = new array_t();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case (value_t::string):
|
||
|
{
|
||
|
m_value.string = new string_t();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case (value_t::boolean):
|
||
|
{
|
||
|
m_value.boolean = boolean_t();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case (value_t::number_integer):
|
||
|
{
|
||
|
m_value.number_integer = number_integer_t();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case (value_t::number_float):
|
||
|
{
|
||
|
m_value.number_float = number_float_t();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// create a null object (implicitly)
|
||
|
inline basic_json() noexcept
|
||
|
: m_type(value_t::null)
|
||
|
{}
|
||
|
|
||
|
/// create a null object (explicitly)
|
||
|
inline basic_json(std::nullptr_t) noexcept
|
||
|
: m_type(value_t::null)
|
||
|
{}
|
||
|
|
||
|
/// create an object (explicit)
|
||
|
inline basic_json(const object_t& value)
|
||
|
: m_type(value_t::object), m_value(new object_t(value))
|
||
|
{}
|
||
|
|
||
|
/// create an object (implicit)
|
||
|
template <class V, typename
|
||
|
std::enable_if<
|
||
|
std::is_constructible<string_t, typename V::key_type>::value and
|
||
|
std::is_constructible<basic_json, typename V::mapped_type>::value, int>::type
|
||
|
= 0>
|
||
|
inline basic_json(const V& value)
|
||
|
: m_type(value_t::object), m_value(new object_t(value.begin(), value.end()))
|
||
|
{}
|
||
|
|
||
|
/// create an array (explicit)
|
||
|
inline basic_json(const array_t& value)
|
||
|
: m_type(value_t::array), m_value(new array_t(value))
|
||
|
{}
|
||
|
|
||
|
/// create an array (implicit)
|
||
|
template <class V, typename
|
||
|
std::enable_if<
|
||
|
not std::is_same<V, basic_json::iterator>::value and
|
||
|
not std::is_same<V, basic_json::const_iterator>::value and
|
||
|
std::is_constructible<basic_json, typename V::value_type>::value, int>::type
|
||
|
= 0>
|
||
|
inline basic_json(const V& value)
|
||
|
: m_type(value_t::array), m_value(new array_t(value.begin(), value.end()))
|
||
|
{}
|
||
|
|
||
|
/// create a string (explicit)
|
||
|
inline basic_json(const string_t& value)
|
||
|
: m_type(value_t::string), m_value(new string_t(value))
|
||
|
{}
|
||
|
|
||
|
/// create a string (explicit)
|
||
|
inline basic_json(const typename string_t::value_type* value)
|
||
|
: m_type(value_t::string), m_value(new string_t(value))
|
||
|
{}
|
||
|
|
||
|
/// create a string (implicit)
|
||
|
template <class V, typename
|
||
|
std::enable_if<
|
||
|
std::is_constructible<string_t, V>::value, int>::type
|
||
|
= 0>
|
||
|
inline basic_json(const V& value)
|
||
|
: basic_json(string_t(value))
|
||
|
{}
|
||
|
|
||
|
/// create a boolean (explicit)
|
||
|
inline basic_json(boolean_t value)
|
||
|
: m_type(value_t::boolean), m_value(value)
|
||
|
{}
|
||
|
|
||
|
/// create an integer number (explicit)
|
||
|
inline basic_json(const number_integer_t& value)
|
||
|
: m_type(value_t::number_integer), m_value(value)
|
||
|
{}
|
||
|
|
||
|
/// create an integer number (implicit)
|
||
|
template<typename T, typename
|
||
|
std::enable_if<
|
||
|
std::is_constructible<number_integer_t, T>::value and
|
||
|
std::numeric_limits<T>::is_integer, T>::type
|
||
|
= 0>
|
||
|
inline basic_json(const T value) noexcept
|
||
|
: m_type(value_t::number_integer), m_value(number_integer_t(value))
|
||
|
{}
|
||
|
|
||
|
/// create a floating point number (explicit)
|
||
|
inline basic_json(const number_float_t& value)
|
||
|
: m_type(value_t::number_float), m_value(value)
|
||
|
{}
|
||
|
|
||
|
/// create a floating point number (implicit)
|
||
|
template<typename T, typename = typename
|
||
|
std::enable_if<
|
||
|
std::is_constructible<number_float_t, T>::value and
|
||
|
std::is_floating_point<T>::value>::type
|
||
|
>
|
||
|
inline basic_json(const T value) noexcept
|
||
|
: m_type(value_t::number_float), m_value(number_float_t(value))
|
||
|
{}
|
||
|
|
||
|
inline basic_json(list_init_t l, bool type_deduction = true, value_t manual_type = value_t::array)
|
||
|
{
|
||
|
// the initializer list could describe an object
|
||
|
bool is_object = true;
|
||
|
|
||
|
// check if each element is an array with two elements whose first element
|
||
|
// is a string
|
||
|
for (const auto& element : l)
|
||
|
{
|
||
|
if ((element.m_final and element.m_type == value_t::array)
|
||
|
or (element.m_type != value_t::array or element.size() != 2
|
||
|
or element[0].m_type != value_t::string))
|
||
|
{
|
||
|
// we found an element that makes it impossible to use the
|
||
|
// initializer list as object
|
||
|
is_object = false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// adjust type if type deduction is not wanted
|
||
|
if (not type_deduction)
|
||
|
{
|
||
|
// mark this object's type as final
|
||
|
m_final = true;
|
||
|
|
||
|
// if array is wanted, do not create an object though possible
|
||
|
if (manual_type == value_t::array)
|
||
|
{
|
||
|
is_object = false;
|
||
|
}
|
||
|
|
||
|
// if object is wanted but impossible, throw an exception
|
||
|
if (manual_type == value_t::object and not is_object)
|
||
|
{
|
||
|
throw std::logic_error("cannot create JSON object");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (is_object)
|
||
|
{
|
||
|
// the initializer list is a list of pairs -> create object
|
||
|
m_type = value_t::object;
|
||
|
m_value = new object_t();
|
||
|
for (auto& element : l)
|
||
|
{
|
||
|
m_value.object->emplace(std::move(*(element[0].m_value.string)), std::move(element[1]));
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// the initializer list describes an array -> create array
|
||
|
m_type = value_t::array;
|
||
|
m_value = new array_t(std::move(l));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inline static basic_json array(list_init_t l = list_init_t())
|
||
|
{
|
||
|
return basic_json(l, false, value_t::array);
|
||
|
}
|
||
|
|
||
|
inline static basic_json object(list_init_t l = list_init_t())
|
||
|
{
|
||
|
// if more than one element is in the initializer list, wrap it
|
||
|
if (l.size() > 1)
|
||
|
{
|
||
|
return basic_json({l}, false, value_t::object);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return basic_json(l, false, value_t::object);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////
|
||
|
// other constructors and destructor //
|
||
|
///////////////////////////////////////
|
||
|
|
||
|
/// copy constructor
|
||
|
inline basic_json(const basic_json& other)
|
||
|
: m_type(other.m_type)
|
||
|
{
|
||
|
switch (m_type)
|
||
|
{
|
||
|
case (value_t::null):
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
case (value_t::object):
|
||
|
{
|
||
|
m_value.object = new object_t(*other.m_value.object);
|
||
|
break;
|
||
|
}
|
||
|
case (value_t::array):
|
||
|
{
|
||
|
m_value.array = new array_t(*other.m_value.array);
|
||
|
break;
|
||
|
}
|
||
|
case (value_t::string):
|
||
|
{
|
||
|
m_value.string = new string_t(*other.m_value.string);
|
||
|
break;
|
||
|
}
|
||
|
case (value_t::boolean):
|
||
|
{
|
||
|
m_value.boolean = other.m_value.boolean;
|
||
|
break;
|
||
|
}
|
||
|
case (value_t::number_integer):
|
||
|
{
|
||
|
m_value.number_integer = other.m_value.number_integer;
|
||
|
break;
|
||
|
}
|
||
|
case (value_t::number_float):
|
||
|
{
|
||
|
m_value.number_float = other.m_value.number_float;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// move constructor
|
||
|
inline basic_json(basic_json&& other) noexcept
|
||
|
: m_type(std::move(other.m_type)),
|
||
|
m_value(std::move(other.m_value))
|
||
|
{
|
||
|
// invaludate payload
|
||
|
other.m_type = value_t::null;
|
||
|
other.m_value = {};
|
||
|
}
|
||
|
|
||
|
/// copy assignment
|
||
|
inline reference operator=(basic_json other) noexcept
|
||
|
{
|
||
|
std::swap(m_type, other.m_type);
|
||
|
std::swap(m_value, other.m_value);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
/// destructor
|
||
|
inline ~basic_json() noexcept
|
||
|
{
|
||
|
switch (m_type)
|
||
|
{
|
||
|
case (value_t::object):
|
||
|
{
|
||
|
delete m_value.object;
|
||
|
m_value.object = nullptr;
|
||
|
break;
|
||
|
}
|
||
|
case (value_t::array):
|
||
|
{
|
||
|
delete m_value.array;
|
||
|
m_value.array = nullptr;
|
||
|
break;
|
||
|
}
|
||
|
case (value_t::string):
|
||
|
{
|
||
|
delete m_value.string;
|
||
|
m_value.string = nullptr;
|
||
|
break;
|
||
|
}
|
||
|
case (value_t::null):
|
||
|
case (value_t::boolean):
|
||
|
case (value_t::number_integer):
|
||
|
case (value_t::number_float):
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
public:
|
||
|
///////////////////////
|
||
|
// object inspection //
|
||
|
///////////////////////
|
||
|
|
||
|
/*!
|
||
|
Serialization function for JSON objects. The function tries to mimick Python's
|
||
|
@p json.dumps() function, and currently supports its @p indent parameter.
|
||
|
|
||
|
@param indent if indent is nonnegative, then array elements and object members
|
||
|
will be pretty-printed with that indent level. An indent level
|
||
|
of 0 will only insert newlines. -1 (the default) selects the
|
||
|
most compact representation
|
||
|
|
||
|
@see https://docs.python.org/2/library/json.html#json.dump
|
||
|
*/
|
||
|
inline string_t dump(int indent = -1) const noexcept
|
||
|
{
|
||
|
if (indent >= 0)
|
||
|
{
|
||
|
return dump(true, static_cast<unsigned int>(indent));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return dump(false, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// return the type of the object explicitly
|
||
|
inline value_t type() const noexcept
|
||
|
{
|
||
|
return m_type;
|
||
|
}
|
||
|
|
||
|
/// return the type of the object implicitly
|
||
|
operator value_t() const noexcept
|
||
|
{
|
||
|
return m_type;
|
||
|
}
|
||
|
|
||
|
|
||
|
//////////////////////
|
||
|
// value conversion //
|
||
|
//////////////////////
|
||
|
|
||
|
/// get an object
|
||
|
template <class T, typename
|
||
|
std::enable_if<
|
||
|
std::is_constructible<string_t, typename T::key_type>::value and
|
||
|
std::is_constructible<basic_json, typename T::mapped_type>::value, int>::type
|
||
|
= 0>
|
||
|
inline T get() const
|
||
|
{
|
||
|
switch (m_type)
|
||
|
{
|
||
|
case (value_t::object):
|
||
|
return T(m_value.object->begin(), m_value.object->end());
|
||
|
default:
|
||
|
throw std::logic_error("cannot cast " + type_name() + " to " + typeid(T).name());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// get an array
|
||
|
template <class T, typename
|
||
|
std::enable_if<
|
||
|
not std::is_same<T, string_t>::value and
|
||
|
std::is_constructible<basic_json, typename T::value_type>::value, int>::type
|
||
|
= 0>
|
||
|
inline T get() const
|
||
|
{
|
||
|
switch (m_type)
|
||
|
{
|
||
|
case (value_t::array):
|
||
|
return T(m_value.array->begin(), m_value.array->end());
|
||
|
default:
|
||
|
throw std::logic_error("cannot cast " + type_name() + " to " + typeid(T).name());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// get a string
|
||
|
template <typename T, typename
|
||
|
std::enable_if<
|
||
|
std::is_constructible<T, string_t>::value, int>::type
|
||
|
= 0>
|
||
|
inline T get() const
|
||
|
{
|
||
|
switch (m_type)
|
||
|
{
|
||
|
case (value_t::string):
|
||
|
return *m_value.string;
|
||
|
default:
|
||
|
throw std::logic_error("cannot cast " + type_name() + " to " + typeid(T).name());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// get a boolean
|
||
|
template <typename T, typename
|
||
|
std::enable_if<
|
||
|
std::is_same<boolean_t, T>::value, int>::type
|
||
|
= 0>
|
||
|
inline T get() const
|
||
|
{
|
||
|
switch (m_type)
|
||
|
{
|
||
|
case (value_t::boolean):
|
||
|
return m_value.boolean;
|
||
|
default:
|
||
|
throw std::logic_error("cannot cast " + type_name() + " to " + typeid(T).name());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// explicitly get a number
|
||
|
template<typename T, typename
|
||
|
std::enable_if<
|
||
|
not std::is_same<boolean_t, T>::value and
|
||
|
std::is_arithmetic<T>::value, int>::type
|
||
|
= 0>
|
||
|
inline T get() const
|
||
|
{
|
||
|
switch (m_type)
|
||
|
{
|
||
|
case (value_t::number_integer):
|
||
|
return static_cast<T>(m_value.number_integer);
|
||
|
case (value_t::number_float):
|
||
|
return static_cast<T>(m_value.number_float);
|
||
|
default:
|
||
|
throw std::logic_error("cannot cast " + type_name() + " to " + typeid(T).name());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// explicitly get a value
|
||
|
template<typename T>
|
||
|
inline operator T() const
|
||
|
{
|
||
|
return get<T>();
|
||
|
}
|
||
|
|
||
|
|
||
|
////////////////////
|
||
|
// element access //
|
||
|
////////////////////
|
||
|
|
||
|
/// access specified element with bounds checking
|
||
|
inline reference at(size_type pos)
|
||
|
{
|
||
|
// at only works for arrays
|
||
|
if (m_type != value_t::array)
|
||
|
{
|
||
|
throw std::runtime_error("cannot use at with " + type_name());
|
||
|
}
|
||
|
|
||
|
return m_value.array->at(pos);
|
||
|
}
|
||
|
|
||
|
/// access specified element with bounds checking
|
||
|
inline const_reference at(size_type pos) const
|
||
|
{
|
||
|
// at only works for arrays
|
||
|
if (m_type != value_t::array)
|
||
|
{
|
||
|
throw std::runtime_error("cannot use at with " + type_name());
|
||
|
}
|
||
|
|
||
|
return m_value.array->at(pos);
|
||
|
}
|
||
|
|
||
|
/// access specified element
|
||
|
inline reference operator[](size_type pos)
|
||
|
{
|
||
|
// at only works for arrays
|
||
|
if (m_type != value_t::array)
|
||
|
{
|
||
|
throw std::runtime_error("cannot use [] with " + type_name());
|
||
|
}
|
||
|
|
||
|
return m_value.array->operator[](pos);
|
||
|
}
|
||
|
|
||
|
/// access specified element
|
||
|
inline const_reference operator[](size_type pos) const
|
||
|
{
|
||
|
// at only works for arrays
|
||
|
if (m_type != value_t::array)
|
||
|
{
|
||
|
throw std::runtime_error("cannot use [] with " + type_name());
|
||
|
}
|
||
|
|
||
|
return m_value.array->operator[](pos);
|
||
|
}
|
||
|
|
||
|
/// access specified element with bounds checking
|
||
|
inline reference at(const typename object_t::key_type& key)
|
||
|
{
|
||
|
// at only works for objects
|
||
|
if (m_type != value_t::object)
|
||
|
{
|
||
|
throw std::runtime_error("cannot use at with " + type_name());
|
||
|
}
|
||
|
|
||
|
return m_value.object->at(key);
|
||
|
}
|
||
|
|
||
|
/// access specified element with bounds checking
|
||
|
inline const_reference at(const typename object_t::key_type& key) const
|
||
|
{
|
||
|
// at only works for objects
|
||
|
if (m_type != value_t::object)
|
||
|
{
|
||
|
throw std::runtime_error("cannot use at with " + type_name());
|
||
|
}
|
||
|
|
||
|
return m_value.object->at(key);
|
||
|
}
|
||
|
|
||
|
/// access specified element
|
||
|
inline reference operator[](const typename object_t::key_type& key)
|
||
|
{
|
||
|
// at only works for objects
|
||
|
if (m_type != value_t::object)
|
||
|
{
|
||
|
throw std::runtime_error("cannot use [] with " + type_name());
|
||
|
}
|
||
|
|
||
|
return m_value.object->operator[](key);
|
||
|
}
|
||
|
|
||
|
/// access specified element (needed for clang)
|
||
|
template<typename T, size_t n>
|
||
|
inline reference operator[](const T (&key)[n])
|
||
|
{
|
||
|
// at only works for objects
|
||
|
if (m_type != value_t::object)
|
||
|
{
|
||
|
throw std::runtime_error("cannot use [] with " + type_name());
|
||
|
}
|
||
|
|
||
|
return m_value.object->operator[](key);
|
||
|
}
|
||
|
|
||
|
/// access specified element
|
||
|
inline reference operator[](typename object_t::key_type&& key)
|
||
|
{
|
||
|
// at only works for objects
|
||
|
if (m_type != value_t::object)
|
||
|
{
|
||
|
throw std::runtime_error("cannot use [] with " + type_name());
|
||
|
}
|
||
|
|
||
|
return m_value.object->operator[](std::move(key));
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////
|
||
|
// iterators //
|
||
|
///////////////
|
||
|
|
||
|
/// returns an iterator to the beginning of the container
|
||
|
inline iterator begin() noexcept
|
||
|
{
|
||
|
iterator result(this);
|
||
|
result.set_begin();
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/// returns a const iterator to the beginning of the container
|
||
|
inline const_iterator begin() const noexcept
|
||
|
{
|
||
|
const_iterator result(this);
|
||
|
result.set_begin();
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/// returns a const iterator to the beginning of the container
|
||
|
inline const_iterator cbegin() const noexcept
|
||
|
{
|
||
|
const_iterator result(this);
|
||
|
result.set_begin();
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/// returns an iterator to the end of the container
|
||
|
inline iterator end() noexcept
|
||
|
{
|
||
|
iterator result(this);
|
||
|
result.set_end();
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/// returns a const iterator to the end of the container
|
||
|
inline const_iterator end() const noexcept
|
||
|
{
|
||
|
const_iterator result(this);
|
||
|
result.set_end();
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/// returns a const iterator to the end of the container
|
||
|
inline const_iterator cend() const noexcept
|
||
|
{
|
||
|
const_iterator result(this);
|
||
|
result.set_end();
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
//////////////
|
||
|
// capacity //
|
||
|
//////////////
|
||
|
|
||
|
/// checks whether the container is empty
|
||
|
inline bool empty() const noexcept
|
||
|
{
|
||
|
switch (m_type)
|
||
|
{
|
||
|
case (value_t::null):
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
case (value_t::number_integer):
|
||
|
case (value_t::number_float):
|
||
|
case (value_t::boolean):
|
||
|
case (value_t::string):
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
case (value_t::array):
|
||
|
{
|
||
|
return m_value.array->empty();
|
||
|
}
|
||
|
case (value_t::object):
|
||
|
{
|
||
|
return m_value.object->empty();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// returns the number of elements
|
||
|
inline size_type size() const noexcept
|
||
|
{
|
||
|
switch (m_type)
|
||
|
{
|
||
|
case (value_t::null):
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
case (value_t::number_integer):
|
||
|
case (value_t::number_float):
|
||
|
case (value_t::boolean):
|
||
|
case (value_t::string):
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
case (value_t::array):
|
||
|
{
|
||
|
return m_value.array->size();
|
||
|
}
|
||
|
case (value_t::object):
|
||
|
{
|
||
|
return m_value.object->size();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// returns the maximum possible number of elements
|
||
|
inline size_type max_size() const noexcept
|
||
|
{
|
||
|
switch (m_type)
|
||
|
{
|
||
|
case (value_t::null):
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
case (value_t::number_integer):
|
||
|
case (value_t::number_float):
|
||
|
case (value_t::boolean):
|
||
|
case (value_t::string):
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
case (value_t::array):
|
||
|
{
|
||
|
return m_value.array->max_size();
|
||
|
}
|
||
|
case (value_t::object):
|
||
|
{
|
||
|
return m_value.object->max_size();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////
|
||
|
// modifiers //
|
||
|
///////////////
|
||
|
|
||
|
/// clears the contents
|
||
|
inline void clear() noexcept
|
||
|
{
|
||
|
switch (m_type)
|
||
|
{
|
||
|
case (value_t::null):
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
case (value_t::number_integer):
|
||
|
{
|
||
|
m_value.number_integer = {};
|
||
|
break;
|
||
|
}
|
||
|
case (value_t::number_float):
|
||
|
{
|
||
|
m_value.number_float = {};
|
||
|
break;
|
||
|
}
|
||
|
case (value_t::boolean):
|
||
|
{
|
||
|
m_value.boolean = {};
|
||
|
break;
|
||
|
}
|
||
|
case (value_t::string):
|
||
|
{
|
||
|
m_value.string->clear();
|
||
|
break;
|
||
|
}
|
||
|
case (value_t::array):
|
||
|
{
|
||
|
m_value.array->clear();
|
||
|
break;
|
||
|
}
|
||
|
case (value_t::object):
|
||
|
{
|
||
|
m_value.object->clear();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// add an object to an array
|
||
|
inline void push_back(basic_json&& value)
|
||
|
{
|
||
|
// push_back only works for null objects or arrays
|
||
|
if (not(m_type == value_t::null or m_type == value_t::array))
|
||
|
{
|
||
|
throw std::runtime_error("cannot add element to " + type_name());
|
||
|
}
|
||
|
|
||
|
// transform null object into an array
|
||
|
if (m_type == value_t::null)
|
||
|
{
|
||
|
m_type = value_t::array;
|
||
|
m_value.array = new array_t;
|
||
|
}
|
||
|
|
||
|
// add element to array (move semantics)
|
||
|
m_value.array->push_back(std::move(value));
|
||
|
// invalidate object
|
||
|
value.m_type = value_t::null;
|
||
|
}
|
||
|
|
||
|
/// add an object to an array
|
||
|
inline void push_back(const basic_json& value)
|
||
|
{
|
||
|
// push_back only works for null objects or arrays
|
||
|
if (not(m_type == value_t::null or m_type == value_t::array))
|
||
|
{
|
||
|
throw std::runtime_error("cannot add element to " + type_name());
|
||
|
}
|
||
|
|
||
|
// transform null object into an array
|
||
|
if (m_type == value_t::null)
|
||
|
{
|
||
|
m_type = value_t::array;
|
||
|
m_value.array = new array_t;
|
||
|
}
|
||
|
|
||
|
// add element to array
|
||
|
m_value.array->push_back(value);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
/// add an object to an array
|
||
|
inline reference operator+=(const basic_json& value)
|
||
|
{
|
||
|
push_back(value);
|
||
|
return *this;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
/// add constructible objects to an array
|
||
|
template<class T, typename std::enable_if<std::is_constructible<basic_json, T>::value>::type = 0>
|
||
|
inline void push_back(const T& value)
|
||
|
{
|
||
|
assert(false); // not sure if function will ever be called
|
||
|
push_back(basic_json(value));
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
/// add constructible objects to an array
|
||
|
template<class T, typename std::enable_if<std::is_constructible<basic_json, T>::value>::type = 0>
|
||
|
inline reference operator+=(const T& value)
|
||
|
{
|
||
|
push_back(basic_json(value));
|
||
|
return *this;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
/// add an object to an object
|
||
|
inline void push_back(const typename object_t::value_type& value)
|
||
|
{
|
||
|
// push_back only works for null objects or objects
|
||
|
if (not(m_type == value_t::null or m_type == value_t::object))
|
||
|
{
|
||
|
throw std::runtime_error("cannot add element to " + type_name());
|
||
|
}
|
||
|
|
||
|
// transform null object into an object
|
||
|
if (m_type == value_t::null)
|
||
|
{
|
||
|
m_type = value_t::object;
|
||
|
m_value.object = new object_t;
|
||
|
}
|
||
|
|
||
|
// add element to array
|
||
|
m_value.object->insert(value);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
/// add an object to an object
|
||
|
inline reference operator+=(const typename object_t::value_type& value)
|
||
|
{
|
||
|
push_back(value);
|
||
|
return operator[](value.first);
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
/// constructs element in-place at the end of an array
|
||
|
template <typename T, typename
|
||
|
std::enable_if<
|
||
|
std::is_constructible<basic_json, T>::value, int>::type
|
||
|
= 0>
|
||
|
inline void emplace_back(T && arg)
|
||
|
{
|
||
|
// push_back only works for null objects or arrays
|
||
|
if (not(m_type == value_t::null or m_type == value_t::array))
|
||
|
{
|
||
|
throw std::runtime_error("cannot add element to " + type_name());
|
||
|
}
|
||
|
|
||
|
// transform null object into an array
|
||
|
if (m_type == value_t::null)
|
||
|
{
|
||
|
m_type = value_t::array;
|
||
|
m_value.array = new array_t;
|
||
|
}
|
||
|
|
||
|
// add element to array
|
||
|
m_value.array->emplace_back(std::forward<T>(arg));
|
||
|
}
|
||
|
|
||
|
/// swaps the contents
|
||
|
inline void swap(reference other) noexcept
|
||
|
{
|
||
|
std::swap(m_type, other.m_type);
|
||
|
std::swap(m_value, other.m_value);
|
||
|
}
|
||
|
|
||
|
/// swaps the contents
|
||
|
inline void swap(array_t& other)
|
||
|
{
|
||
|
// swap only works for arrays
|
||
|
if (m_type != value_t::array)
|
||
|
{
|
||
|
throw std::runtime_error("cannot use swap with " + type_name());
|
||
|
}
|
||
|
|
||
|
// swap arrays
|
||
|
std::swap(*(m_value.array), other);
|
||
|
}
|
||
|
|
||
|
/// swaps the contents
|
||
|
inline void swap(object_t& other)
|
||
|
{
|
||
|
// swap only works for objects
|
||
|
if (m_type != value_t::object)
|
||
|
{
|
||
|
throw std::runtime_error("cannot use swap with " + type_name());
|
||
|
}
|
||
|
|
||
|
// swap arrays
|
||
|
std::swap(*(m_value.object), other);
|
||
|
}
|
||
|
|
||
|
/// swaps the contents
|
||
|
inline void swap(string_t& other)
|
||
|
{
|
||
|
// swap only works for strings
|
||
|
if (m_type != value_t::string)
|
||
|
{
|
||
|
throw std::runtime_error("cannot use swap with " + type_name());
|
||
|
}
|
||
|
|
||
|
// swap arrays
|
||
|
std::swap(*(m_value.string), other);
|
||
|
}
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////
|
||
|
// lexicographical comparison operators //
|
||
|
//////////////////////////////////////////
|
||
|
|
||
|
/// comparison: equal
|
||
|
friend bool operator==(const_reference lhs, const_reference rhs)
|
||
|
{
|
||
|
switch (lhs.type())
|
||
|
{
|
||
|
case (value_t::array):
|
||
|
{
|
||
|
if (rhs.type() == value_t::array)
|
||
|
{
|
||
|
return *lhs.m_value.array == *rhs.m_value.array;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case (value_t::object):
|
||
|
{
|
||
|
if (rhs.type() == value_t::object)
|
||
|
{
|
||
|
return *lhs.m_value.object == *rhs.m_value.object;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case (value_t::null):
|
||
|
{
|
||
|
if (rhs.type() == value_t::null)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case (value_t::string):
|
||
|
{
|
||
|
if (rhs.type() == value_t::string)
|
||
|
{
|
||
|
return *lhs.m_value.string == *rhs.m_value.string;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case (value_t::boolean):
|
||
|
{
|
||
|
if (rhs.type() == value_t::boolean)
|
||
|
{
|
||
|
return lhs.m_value.boolean == rhs.m_value.boolean;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case (value_t::number_integer):
|
||
|
{
|
||
|
if (rhs.type() == value_t::number_integer)
|
||
|
{
|
||
|
return lhs.m_value.number_integer == rhs.m_value.number_integer;
|
||
|
}
|
||
|
if (rhs.type() == value_t::number_float)
|
||
|
{
|
||
|
return lhs.m_value.number_integer == static_cast<number_integer_t>(rhs.m_value.number_float);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case (value_t::number_float):
|
||
|
{
|
||
|
if (rhs.type() == value_t::number_integer)
|
||
|
{
|
||
|
return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_integer);
|
||
|
}
|
||
|
if (rhs.type() == value_t::number_float)
|
||
|
{
|
||
|
return lhs.m_value.number_float == rhs.m_value.number_float;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/// comparison: not equal
|
||
|
friend bool operator!=(const_reference lhs, const_reference rhs)
|
||
|
{
|
||
|
return not (lhs == rhs);
|
||
|
}
|
||
|
|
||
|
/// comparison: less than
|
||
|
friend bool operator<(const_reference lhs, const_reference rhs)
|
||
|
{
|
||
|
switch (lhs.type())
|
||
|
{
|
||
|
case (value_t::array):
|
||
|
{
|
||
|
if (rhs.type() == value_t::array)
|
||
|
{
|
||
|
return *lhs.m_value.array < *rhs.m_value.array;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case (value_t::object):
|
||
|
{
|
||
|
if (rhs.type() == value_t::object)
|
||
|
{
|
||
|
return *lhs.m_value.object < *rhs.m_value.object;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case (value_t::null):
|
||
|
{
|
||
|
if (rhs.type() == value_t::null)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case (value_t::string):
|
||
|
{
|
||
|
if (rhs.type() == value_t::string)
|
||
|
{
|
||
|
return *lhs.m_value.string < *rhs.m_value.string;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case (value_t::boolean):
|
||
|
{
|
||
|
if (rhs.type() == value_t::boolean)
|
||
|
{
|
||
|
return lhs.m_value.boolean < rhs.m_value.boolean;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case (value_t::number_integer):
|
||
|
{
|
||
|
if (rhs.type() == value_t::number_integer)
|
||
|
{
|
||
|
return lhs.m_value.number_integer < rhs.m_value.number_integer;
|
||
|
}
|
||
|
if (rhs.type() == value_t::number_float)
|
||
|
{
|
||
|
return lhs.m_value.number_integer < static_cast<number_integer_t>(rhs.m_value.number_float);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case (value_t::number_float):
|
||
|
{
|
||
|
if (rhs.type() == value_t::number_integer)
|
||
|
{
|
||
|
return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_integer);
|
||
|
}
|
||
|
if (rhs.type() == value_t::number_float)
|
||
|
{
|
||
|
return lhs.m_value.number_float < rhs.m_value.number_float;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/// comparison: less than or equal
|
||
|
friend bool operator<=(const_reference lhs, const_reference rhs)
|
||
|
{
|
||
|
return not (rhs < lhs);
|
||
|
}
|
||
|
|
||
|
/// comparison: greater than
|
||
|
friend bool operator>(const_reference lhs, const_reference rhs)
|
||
|
{
|
||
|
return not (lhs <= rhs);
|
||
|
}
|
||
|
|
||
|
/// comparison: greater than or equal
|
||
|
friend bool operator>=(const_reference lhs, const_reference rhs)
|
||
|
{
|
||
|
return not (lhs < rhs);
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////
|
||
|
// serialization //
|
||
|
///////////////////
|
||
|
|
||
|
/// serialize to stream
|
||
|
friend std::ostream& operator<<(std::ostream& o, const basic_json& j)
|
||
|
{
|
||
|
o << j.dump();
|
||
|
return o;
|
||
|
}
|
||
|
|
||
|
/// serialize to stream
|
||
|
friend std::ostream& operator>>(const basic_json& j, std::ostream& o)
|
||
|
{
|
||
|
o << j.dump();
|
||
|
return o;
|
||
|
}
|
||
|
|
||
|
|
||
|
private:
|
||
|
///////////////////////////
|
||
|
// convenience functions //
|
||
|
///////////////////////////
|
||
|
|
||
|
/// return the type as string
|
||
|
inline string_t type_name() const noexcept
|
||
|
{
|
||
|
switch (m_type)
|
||
|
{
|
||
|
case (value_t::null):
|
||
|
{
|
||
|
return "null";
|
||
|
}
|
||
|
case (value_t::object):
|
||
|
{
|
||
|
return "object";
|
||
|
}
|
||
|
case (value_t::array):
|
||
|
{
|
||
|
return "array";
|
||
|
}
|
||
|
case (value_t::string):
|
||
|
{
|
||
|
return "string";
|
||
|
}
|
||
|
case (value_t::boolean):
|
||
|
{
|
||
|
return "boolean";
|
||
|
}
|
||
|
case (value_t::number_integer):
|
||
|
case (value_t::number_float):
|
||
|
{
|
||
|
return "number";
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Internal implementation of the serialization function.
|
||
|
|
||
|
@param prettyPrint whether the output shall be pretty-printed
|
||
|
@param indentStep the indent level
|
||
|
@param currentIndent the current indent level (only used internally)
|
||
|
*/
|
||
|
inline string_t dump(const bool prettyPrint, const unsigned int indentStep,
|
||
|
unsigned int currentIndent = 0) const noexcept
|
||
|
{
|
||
|
// helper function to return whitespace as indentation
|
||
|
const auto indent = [prettyPrint, ¤tIndent]()
|
||
|
{
|
||
|
return prettyPrint ? string_t(currentIndent, ' ') : string_t();
|
||
|
};
|
||
|
|
||
|
switch (m_type)
|
||
|
{
|
||
|
case (value_t::null):
|
||
|
{
|
||
|
return "null";
|
||
|
}
|
||
|
|
||
|
case (value_t::object):
|
||
|
{
|
||
|
if (m_value.object->empty())
|
||
|
{
|
||
|
return "{}";
|
||
|
}
|
||
|
|
||
|
string_t result = "{";
|
||
|
|
||
|
// increase indentation
|
||
|
if (prettyPrint)
|
||
|
{
|
||
|
currentIndent += indentStep;
|
||
|
result += "\n";
|
||
|
}
|
||
|
|
||
|
for (typename object_t::const_iterator i = m_value.object->begin(); i != m_value.object->end(); ++i)
|
||
|
{
|
||
|
if (i != m_value.object->begin())
|
||
|
{
|
||
|
result += prettyPrint ? ",\n" : ",";
|
||
|
}
|
||
|
result += indent() + "\"" + i->first + "\":" + (prettyPrint ? " " : "")
|
||
|
+ i->second.dump(prettyPrint, indentStep, currentIndent);
|
||
|
}
|
||
|
|
||
|
// decrease indentation
|
||
|
if (prettyPrint)
|
||
|
{
|
||
|
currentIndent -= indentStep;
|
||
|
result += "\n";
|
||
|
}
|
||
|
|
||
|
return result + indent() + "}";
|
||
|
}
|
||
|
|
||
|
case (value_t::array):
|
||
|
{
|
||
|
if (m_value.array->empty())
|
||
|
{
|
||
|
return "[]";
|
||
|
}
|
||
|
|
||
|
string_t result = "[";
|
||
|
|
||
|
// increase indentation
|
||
|
if (prettyPrint)
|
||
|
{
|
||
|
currentIndent += indentStep;
|
||
|
result += "\n";
|
||
|
}
|
||
|
|
||
|
for (typename array_t::const_iterator i = m_value.array->begin(); i != m_value.array->end(); ++i)
|
||
|
{
|
||
|
if (i != m_value.array->begin())
|
||
|
{
|
||
|
result += prettyPrint ? ",\n" : ",";
|
||
|
}
|
||
|
result += indent() + i->dump(prettyPrint, indentStep, currentIndent);
|
||
|
}
|
||
|
|
||
|
// decrease indentation
|
||
|
if (prettyPrint)
|
||
|
{
|
||
|
currentIndent -= indentStep;
|
||
|
result += "\n";
|
||
|
}
|
||
|
|
||
|
return result + indent() + "]";
|
||
|
}
|
||
|
|
||
|
case (value_t::string):
|
||
|
{
|
||
|
return string_t("\"") + *m_value.string + "\"";
|
||
|
}
|
||
|
|
||
|
case (value_t::boolean):
|
||
|
{
|
||
|
return m_value.boolean ? "true" : "false";
|
||
|
}
|
||
|
|
||
|
case (value_t::number_integer):
|
||
|
{
|
||
|
return std::to_string(m_value.number_integer);
|
||
|
}
|
||
|
|
||
|
case (value_t::number_float):
|
||
|
{
|
||
|
return std::to_string(m_value.number_float);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
private:
|
||
|
//////////////////////
|
||
|
// member variables //
|
||
|
//////////////////////
|
||
|
|
||
|
/// the type of the current element
|
||
|
value_t m_type = value_t::null;
|
||
|
|
||
|
/// whether the type of JSON object may change later
|
||
|
bool m_final = false;
|
||
|
|
||
|
/// the value of the current element
|
||
|
json_value m_value = {};
|
||
|
|
||
|
public:
|
||
|
///////////////
|
||
|
// iterators //
|
||
|
///////////////
|
||
|
|
||
|
/// a bidirectional iterator for the basic_json class
|
||
|
class iterator : public std::iterator<std::bidirectional_iterator_tag, basic_json>
|
||
|
{
|
||
|
public:
|
||
|
/// the type of the values when the iterator is dereferenced
|
||
|
using value_type = basic_json::value_type;
|
||
|
/// a type to represent differences between iterators
|
||
|
using difference_type = basic_json::difference_type;
|
||
|
/// defines a pointer to the type iterated over (value_type)
|
||
|
using pointer = basic_json::pointer;
|
||
|
/// defines a reference to the type iterated over (value_type)
|
||
|
using reference = basic_json::reference;
|
||
|
/// the category of the iterator
|
||
|
using iterator_category = std::bidirectional_iterator_tag;
|
||
|
|
||
|
/// values of a generic iterator type of non-container JSON values
|
||
|
enum class generic_iterator_value
|
||
|
{
|
||
|
/// the iterator was not initialized
|
||
|
uninitialized,
|
||
|
/// the iterator points to the only value
|
||
|
begin,
|
||
|
/// the iterator points past the only value
|
||
|
end
|
||
|
};
|
||
|
|
||
|
/// an iterator value
|
||
|
union internal_iterator
|
||
|
{
|
||
|
/// iterator for JSON objects
|
||
|
typename object_t::iterator object_iterator;
|
||
|
/// iterator for JSON arrays
|
||
|
typename array_t::iterator array_iterator;
|
||
|
/// generic iteraotr for all other value types
|
||
|
generic_iterator_value generic_iterator;
|
||
|
|
||
|
/// default constructor
|
||
|
internal_iterator() : generic_iterator(generic_iterator_value::uninitialized) {}
|
||
|
/// constructor for object iterators
|
||
|
internal_iterator(typename object_t::iterator v) : object_iterator(v) {}
|
||
|
/// constructor for array iterators
|
||
|
internal_iterator(typename array_t::iterator v) : array_iterator(v) {}
|
||
|
/// constructor for generic iterators
|
||
|
internal_iterator(generic_iterator_value v) : generic_iterator(v) {}
|
||
|
};
|
||
|
|
||
|
/// constructor for a given JSON instance
|
||
|
inline iterator(pointer object) : m_object(object)
|
||
|
{
|
||
|
if (m_object == nullptr)
|
||
|
{
|
||
|
throw std::logic_error("cannot create iterator");
|
||
|
}
|
||
|
|
||
|
switch (m_object->m_type)
|
||
|
{
|
||
|
case (basic_json::value_t::object):
|
||
|
{
|
||
|
m_it.object_iterator = typename object_t::iterator();
|
||
|
break;
|
||
|
}
|
||
|
case (basic_json::value_t::array):
|
||
|
{
|
||
|
m_it.array_iterator = typename array_t::iterator();
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
{
|
||
|
m_it.generic_iterator = generic_iterator_value::uninitialized;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// copy assignment
|
||
|
inline iterator& operator=(const iterator& other) noexcept
|
||
|
{
|
||
|
m_object = other.m_object;
|
||
|
m_it = other.m_it;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
/// set the iterator to the first value
|
||
|
inline void set_begin() noexcept
|
||
|
{
|
||
|
switch (m_object->m_type)
|
||
|
{
|
||
|
case (basic_json::value_t::object):
|
||
|
{
|
||
|
m_it.object_iterator = m_object->m_value.object->begin();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case (basic_json::value_t::array):
|
||
|
{
|
||
|
m_it.array_iterator = m_object->m_value.array->begin();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case (basic_json::value_t::null):
|
||
|
{
|
||
|
m_it.generic_iterator = generic_iterator_value::end;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
m_it.generic_iterator = generic_iterator_value::begin;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// set the iterator past the last value
|
||
|
inline void set_end() noexcept
|
||
|
{
|
||
|
switch (m_object->m_type)
|
||
|
{
|
||
|
case (basic_json::value_t::object):
|
||
|
{
|
||
|
m_it.object_iterator = m_object->m_value.object->end();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case (basic_json::value_t::array):
|
||
|
{
|
||
|
m_it.array_iterator = m_object->m_value.array->end();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
m_it.generic_iterator = generic_iterator_value::end;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// return a reference to the value pointed to by the iterator
|
||
|
inline reference operator*() const
|
||
|
{
|
||
|
if (m_object == nullptr)
|
||
|
{
|
||
|
throw std::out_of_range("cannot get value");
|
||
|
}
|
||
|
|
||
|
switch (m_object->m_type)
|
||
|
{
|
||
|
case (basic_json::value_t::object):
|
||
|
{
|
||
|
return m_it.object_iterator->second;
|
||
|
}
|
||
|
|
||
|
case (basic_json::value_t::array):
|
||
|
{
|
||
|
return *m_it.array_iterator;
|
||
|
}
|
||
|
|
||
|
case (basic_json::value_t::null):
|
||
|
{
|
||
|
throw std::out_of_range("cannot get value");
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
if (m_it.generic_iterator == generic_iterator_value::begin)
|
||
|
{
|
||
|
return *m_object;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
throw std::out_of_range("cannot get value");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// dereference the iterator
|
||
|
inline pointer operator->() const
|
||
|
{
|
||
|
if (m_object == nullptr)
|
||
|
{
|
||
|
throw std::out_of_range("cannot get value");
|
||
|
}
|
||
|
|
||
|
switch (m_object->m_type)
|
||
|
{
|
||
|
case (basic_json::value_t::object):
|
||
|
{
|
||
|
return &(m_it.object_iterator->second);
|
||
|
}
|
||
|
|
||
|
case (basic_json::value_t::array):
|
||
|
{
|
||
|
return &*m_it.array_iterator;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
if (m_it.generic_iterator == generic_iterator_value::begin)
|
||
|
{
|
||
|
return m_object;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
throw std::out_of_range("cannot get value");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// post-increment (it++)
|
||
|
inline iterator operator++(int)
|
||
|
{
|
||
|
iterator result = *this;
|
||
|
|
||
|
if (m_object == nullptr)
|
||
|
{
|
||
|
throw std::out_of_range("cannot increment iterator");
|
||
|
}
|
||
|
|
||
|
switch (m_object->m_type)
|
||
|
{
|
||
|
case (basic_json::value_t::object):
|
||
|
{
|
||
|
m_it.object_iterator++;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case (basic_json::value_t::array):
|
||
|
{
|
||
|
m_it.array_iterator++;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
m_it.generic_iterator = generic_iterator_value::end;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/// pre-increment (++it)
|
||
|
inline iterator& operator++()
|
||
|
{
|
||
|
if (m_object == nullptr)
|
||
|
{
|
||
|
throw std::out_of_range("cannot increment iterator");
|
||
|
}
|
||
|
|
||
|
switch (m_object->m_type)
|
||
|
{
|
||
|
case (basic_json::value_t::object):
|
||
|
{
|
||
|
++m_it.object_iterator;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case (basic_json::value_t::array):
|
||
|
{
|
||
|
++m_it.array_iterator;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
m_it.generic_iterator = generic_iterator_value::end;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
/// post-decrement (it--)
|
||
|
inline iterator operator--(int)
|
||
|
{
|
||
|
iterator result = *this;
|
||
|
|
||
|
if (m_object == nullptr)
|
||
|
{
|
||
|
throw std::out_of_range("cannot decrement iterator");
|
||
|
}
|
||
|
|
||
|
switch (m_object->m_type)
|
||
|
{
|
||
|
case (basic_json::value_t::object):
|
||
|
{
|
||
|
m_it.object_iterator--;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case (basic_json::value_t::array):
|
||
|
{
|
||
|
m_it.array_iterator--;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
m_it.generic_iterator = generic_iterator_value::uninitialized;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/// pre-decrement (--it)
|
||
|
inline iterator& operator--()
|
||
|
{
|
||
|
if (m_object == nullptr)
|
||
|
{
|
||
|
throw std::out_of_range("cannot decrement iterator");
|
||
|
}
|
||
|
|
||
|
switch (m_object->m_type)
|
||
|
{
|
||
|
case (basic_json::value_t::object):
|
||
|
{
|
||
|
--m_it.object_iterator;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case (basic_json::value_t::array):
|
||
|
{
|
||
|
--m_it.array_iterator;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
m_it.generic_iterator = generic_iterator_value::uninitialized;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
/// comparison: equal
|
||
|
inline bool operator==(const iterator& other) const
|
||
|
{
|
||
|
if (m_object == nullptr
|
||
|
or other.m_object == nullptr
|
||
|
or m_object != other.m_object
|
||
|
or m_object->m_type != other.m_object->m_type)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
switch (m_object->m_type)
|
||
|
{
|
||
|
case (basic_json::value_t::object):
|
||
|
{
|
||
|
return (m_it.object_iterator == other.m_it.object_iterator);
|
||
|
}
|
||
|
|
||
|
case (basic_json::value_t::array):
|
||
|
{
|
||
|
return (m_it.array_iterator == other.m_it.array_iterator);
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
return (m_it.generic_iterator == other.m_it.generic_iterator);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// comparison: not equal
|
||
|
inline bool operator!=(const iterator& other) const
|
||
|
{
|
||
|
return not operator==(other);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
/// associated JSON instance
|
||
|
pointer m_object = nullptr;
|
||
|
/// the actual iterator of the associated instance
|
||
|
internal_iterator m_it;
|
||
|
};
|
||
|
|
||
|
/// a const bidirectional iterator for the basic_json class
|
||
|
class const_iterator : public std::iterator<std::bidirectional_iterator_tag, const basic_json>
|
||
|
{
|
||
|
public:
|
||
|
/// the type of the values when the iterator is dereferenced
|
||
|
using value_type = basic_json::value_type;
|
||
|
/// a type to represent differences between iterators
|
||
|
using difference_type = basic_json::difference_type;
|
||
|
/// defines a pointer to the type iterated over (value_type)
|
||
|
using pointer = basic_json::const_pointer;
|
||
|
/// defines a reference to the type iterated over (value_type)
|
||
|
using reference = basic_json::const_reference;
|
||
|
/// the category of the iterator
|
||
|
using iterator_category = std::bidirectional_iterator_tag;
|
||
|
|
||
|
/// values of a generic iterator type of non-container JSON values
|
||
|
enum class generic_iterator_value
|
||
|
{
|
||
|
/// the iterator was not initialized
|
||
|
uninitialized,
|
||
|
/// the iterator points to the only value
|
||
|
begin,
|
||
|
/// the iterator points past the only value
|
||
|
end
|
||
|
};
|
||
|
|
||
|
/// an iterator value
|
||
|
union internal_const_iterator
|
||
|
{
|
||
|
/// iterator for JSON objects
|
||
|
typename object_t::const_iterator object_iterator;
|
||
|
/// iterator for JSON arrays
|
||
|
typename array_t::const_iterator array_iterator;
|
||
|
/// generic iteraotr for all other value types
|
||
|
generic_iterator_value generic_iterator;
|
||
|
|
||
|
/// default constructor
|
||
|
internal_const_iterator() : generic_iterator(generic_iterator_value::uninitialized) {}
|
||
|
/// constructor for object iterators
|
||
|
internal_const_iterator(typename object_t::iterator v) : object_iterator(v) {}
|
||
|
/// constructor for array iterators
|
||
|
internal_const_iterator(typename array_t::iterator v) : array_iterator(v) {}
|
||
|
/// constructor for generic iterators
|
||
|
internal_const_iterator(generic_iterator_value v) : generic_iterator(v) {}
|
||
|
};
|
||
|
|
||
|
/// constructor for a given JSON instance
|
||
|
inline const_iterator(pointer object) : m_object(object)
|
||
|
{
|
||
|
if (m_object == nullptr)
|
||
|
{
|
||
|
throw std::logic_error("cannot create iterator");
|
||
|
}
|
||
|
|
||
|
switch (m_object->m_type)
|
||
|
{
|
||
|
case (basic_json::value_t::object):
|
||
|
{
|
||
|
m_it.object_iterator = typename object_t::const_iterator();
|
||
|
break;
|
||
|
}
|
||
|
case (basic_json::value_t::array):
|
||
|
{
|
||
|
m_it.array_iterator = typename array_t::const_iterator();
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
{
|
||
|
m_it.generic_iterator = generic_iterator_value::uninitialized;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inline const_iterator(const iterator& other) : m_object(other.m_object), m_it(other.m_it)
|
||
|
{}
|
||
|
|
||
|
/// copy assignment
|
||
|
inline const_iterator operator=(const const_iterator& other) noexcept
|
||
|
{
|
||
|
m_object = other.m_object;
|
||
|
m_it = other.m_it;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
/// set the iterator to the first value
|
||
|
inline void set_begin() noexcept
|
||
|
{
|
||
|
switch (m_object->m_type)
|
||
|
{
|
||
|
case (basic_json::value_t::object):
|
||
|
{
|
||
|
m_it.object_iterator = m_object->m_value.object->cbegin();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case (basic_json::value_t::array):
|
||
|
{
|
||
|
m_it.array_iterator = m_object->m_value.array->cbegin();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case (basic_json::value_t::null):
|
||
|
{
|
||
|
m_it.generic_iterator = generic_iterator_value::end;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
m_it.generic_iterator = generic_iterator_value::begin;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// set the iterator past the last value
|
||
|
inline void set_end() noexcept
|
||
|
{
|
||
|
switch (m_object->m_type)
|
||
|
{
|
||
|
case (basic_json::value_t::object):
|
||
|
{
|
||
|
m_it.object_iterator = m_object->m_value.object->cend();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case (basic_json::value_t::array):
|
||
|
{
|
||
|
m_it.array_iterator = m_object->m_value.array->cend();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
m_it.generic_iterator = generic_iterator_value::end;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// return a reference to the value pointed to by the iterator
|
||
|
inline reference operator*() const
|
||
|
{
|
||
|
if (m_object == nullptr)
|
||
|
{
|
||
|
throw std::out_of_range("cannot get value");
|
||
|
}
|
||
|
|
||
|
switch (m_object->m_type)
|
||
|
{
|
||
|
case (basic_json::value_t::object):
|
||
|
{
|
||
|
return m_it.object_iterator->second;
|
||
|
}
|
||
|
|
||
|
case (basic_json::value_t::array):
|
||
|
{
|
||
|
return *m_it.array_iterator;
|
||
|
}
|
||
|
|
||
|
case (basic_json::value_t::null):
|
||
|
{
|
||
|
throw std::out_of_range("cannot get value");
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
if (m_it.generic_iterator == generic_iterator_value::begin)
|
||
|
{
|
||
|
return *m_object;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
throw std::out_of_range("cannot get value");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// dereference the iterator
|
||
|
inline pointer operator->() const
|
||
|
{
|
||
|
if (m_object == nullptr)
|
||
|
{
|
||
|
throw std::out_of_range("cannot get value");
|
||
|
}
|
||
|
|
||
|
switch (m_object->m_type)
|
||
|
{
|
||
|
case (basic_json::value_t::object):
|
||
|
{
|
||
|
return &(m_it.object_iterator->second);
|
||
|
}
|
||
|
|
||
|
case (basic_json::value_t::array):
|
||
|
{
|
||
|
return &*m_it.array_iterator;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
if (m_it.generic_iterator == generic_iterator_value::begin)
|
||
|
{
|
||
|
return m_object;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
throw std::out_of_range("cannot get value");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// post-increment (it++)
|
||
|
inline const_iterator operator++(int)
|
||
|
{
|
||
|
const_iterator result = *this;
|
||
|
|
||
|
if (m_object == nullptr)
|
||
|
{
|
||
|
throw std::out_of_range("cannot increment iterator");
|
||
|
}
|
||
|
|
||
|
switch (m_object->m_type)
|
||
|
{
|
||
|
case (basic_json::value_t::object):
|
||
|
{
|
||
|
m_it.object_iterator++;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case (basic_json::value_t::array):
|
||
|
{
|
||
|
m_it.array_iterator++;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
m_it.generic_iterator = generic_iterator_value::end;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/// pre-increment (++it)
|
||
|
inline const_iterator& operator++()
|
||
|
{
|
||
|
if (m_object == nullptr)
|
||
|
{
|
||
|
throw std::out_of_range("cannot increment iterator");
|
||
|
}
|
||
|
|
||
|
switch (m_object->m_type)
|
||
|
{
|
||
|
case (basic_json::value_t::object):
|
||
|
{
|
||
|
++m_it.object_iterator;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case (basic_json::value_t::array):
|
||
|
{
|
||
|
++m_it.array_iterator;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
m_it.generic_iterator = generic_iterator_value::end;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
/// post-decrement (it--)
|
||
|
inline const_iterator operator--(int)
|
||
|
{
|
||
|
iterator result = *this;
|
||
|
|
||
|
if (m_object == nullptr)
|
||
|
{
|
||
|
throw std::out_of_range("cannot decrement iterator");
|
||
|
}
|
||
|
|
||
|
switch (m_object->m_type)
|
||
|
{
|
||
|
case (basic_json::value_t::object):
|
||
|
{
|
||
|
m_it.object_iterator--;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case (basic_json::value_t::array):
|
||
|
{
|
||
|
m_it.array_iterator--;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
m_it.generic_iterator = generic_iterator_value::uninitialized;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/// pre-decrement (--it)
|
||
|
inline const_iterator& operator--()
|
||
|
{
|
||
|
if (m_object == nullptr)
|
||
|
{
|
||
|
throw std::out_of_range("cannot decrement iterator");
|
||
|
}
|
||
|
|
||
|
switch (m_object->m_type)
|
||
|
{
|
||
|
case (basic_json::value_t::object):
|
||
|
{
|
||
|
--m_it.object_iterator;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case (basic_json::value_t::array):
|
||
|
{
|
||
|
--m_it.array_iterator;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
m_it.generic_iterator = generic_iterator_value::uninitialized;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
/// comparison: equal
|
||
|
inline bool operator==(const const_iterator& other) const
|
||
|
{
|
||
|
if (m_object == nullptr
|
||
|
or other.m_object == nullptr
|
||
|
or m_object != other.m_object
|
||
|
or m_object->m_type != other.m_object->m_type)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
switch (m_object->m_type)
|
||
|
{
|
||
|
case (basic_json::value_t::object):
|
||
|
{
|
||
|
return (m_it.object_iterator == other.m_it.object_iterator);
|
||
|
}
|
||
|
|
||
|
case (basic_json::value_t::array):
|
||
|
{
|
||
|
return (m_it.array_iterator == other.m_it.array_iterator);
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
return (m_it.generic_iterator == other.m_it.generic_iterator);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// comparison: not equal
|
||
|
inline bool operator!=(const const_iterator& other) const
|
||
|
{
|
||
|
return not operator==(other);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
/// associated JSON instance
|
||
|
pointer m_object = nullptr;
|
||
|
/// the actual iterator of the associated instance
|
||
|
internal_const_iterator m_it;
|
||
|
};
|
||
|
};
|
||
|
|
||
|
|
||
|
/////////////
|
||
|
// presets //
|
||
|
/////////////
|
||
|
|
||
|
/// default JSON class
|
||
|
using json = basic_json<>;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/////////////////////////
|
||
|
// nonmember functions //
|
||
|
/////////////////////////
|
||
|
|
||
|
// specialization of std::swap, and std::hash
|
||
|
namespace std
|
||
|
{
|
||
|
/// swaps the values of two JSON objects
|
||
|
template <>
|
||
|
inline void swap(nlohmann::json& j1,
|
||
|
nlohmann::json& j2) noexcept(
|
||
|
is_nothrow_move_constructible<nlohmann::json>::value and
|
||
|
is_nothrow_move_assignable<nlohmann::json>::value
|
||
|
)
|
||
|
{
|
||
|
j1.swap(j2);
|
||
|
}
|
||
|
|
||
|
/// hash value for JSON objects
|
||
|
template <>
|
||
|
struct hash<nlohmann::json>
|
||
|
{
|
||
|
size_t operator()(const nlohmann::json& j) const
|
||
|
{
|
||
|
// a naive hashing via the string representation
|
||
|
return hash<std::string>()(j.dump());
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
#endif
|