2019-03-31 01:37:06 +08:00
|
|
|
// Copyright (c) 2016-2018 Easy2D - Nomango
|
|
|
|
|
//
|
|
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
|
|
|
// in the Software without restriction, including without limitation the rights
|
|
|
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
|
|
|
// furnished to do so, subject to the following conditions:
|
|
|
|
|
//
|
|
|
|
|
// The above copyright notice and this permission notice shall be included in
|
|
|
|
|
// all copies or substantial portions of the Software.
|
|
|
|
|
//
|
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
|
// THE SOFTWARE.
|
|
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
#include "helper.h"
|
|
|
|
|
#include <cstdint>
|
|
|
|
|
#include <cctype>
|
|
|
|
|
#include <array>
|
|
|
|
|
#include <iosfwd>
|
|
|
|
|
|
|
|
|
|
namespace easy2d
|
|
|
|
|
{
|
|
|
|
|
template <
|
|
|
|
|
template <class _Kty, class _Ty, class ..._Args> typename _ObjectTy = easy2d::Map,
|
|
|
|
|
template <class _Kty, class ..._Args> typename _ArrayTy = easy2d::Array,
|
|
|
|
|
typename _StringTy = easy2d::String,
|
|
|
|
|
typename _IntegerTy = std::int32_t,
|
|
|
|
|
typename _FloatTy = double,
|
|
|
|
|
typename _BooleanTy = bool,
|
|
|
|
|
template <class _Ty> typename _Allocator = std::allocator>
|
|
|
|
|
class basic_json;
|
|
|
|
|
|
|
|
|
|
using Json = basic_json<>;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// details of basic_json
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
#define E2D_DECLARE_BASIC_JSON_TEMPLATE\
|
|
|
|
|
template <\
|
|
|
|
|
template <class _Kty, class _Ty, class ..._Args> typename _ObjectTy, \
|
|
|
|
|
template <class _Kty, class ..._Args> typename _ArrayTy, \
|
|
|
|
|
typename _StringTy, \
|
|
|
|
|
typename _IntegerTy, \
|
|
|
|
|
typename _FloatTy, \
|
|
|
|
|
typename _BooleanTy, \
|
|
|
|
|
template <class _Ty> typename _Allocator>
|
|
|
|
|
|
|
|
|
|
#define E2D_DECLARE_BASIC_JSON_TPL_ARGS \
|
|
|
|
|
_ObjectTy, _ArrayTy, _StringTy, _IntegerTy, _FloatTy, _BooleanTy, _Allocator
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// json value type
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
enum class JsonType
|
|
|
|
|
{
|
|
|
|
|
Integer,
|
|
|
|
|
Float,
|
|
|
|
|
String,
|
|
|
|
|
Array,
|
|
|
|
|
Object,
|
|
|
|
|
Boolean,
|
|
|
|
|
Null,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// is_basic_json
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
template <typename>
|
|
|
|
|
struct is_basic_json
|
|
|
|
|
: ::std::false_type
|
|
|
|
|
{
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
E2D_DECLARE_BASIC_JSON_TEMPLATE
|
|
|
|
|
struct is_basic_json< basic_json<E2D_DECLARE_BASIC_JSON_TPL_ARGS> >
|
|
|
|
|
: ::std::true_type
|
|
|
|
|
{
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// exceptions
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
class json_exception
|
2019-03-31 23:09:49 +08:00
|
|
|
: public std::runtime_error
|
2019-03-31 01:37:06 +08:00
|
|
|
{
|
|
|
|
|
public:
|
2019-03-31 23:09:49 +08:00
|
|
|
json_exception(const char* message)
|
|
|
|
|
: std::runtime_error(message)
|
|
|
|
|
{}
|
2019-03-31 01:37:06 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class json_type_error
|
|
|
|
|
: public json_exception
|
|
|
|
|
{
|
|
|
|
|
public:
|
2019-03-31 23:09:49 +08:00
|
|
|
json_type_error(const char* message) : json_exception(message) {}
|
2019-03-31 01:37:06 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class json_invalid_key
|
|
|
|
|
: public json_exception
|
|
|
|
|
{
|
|
|
|
|
public:
|
2019-03-31 23:09:49 +08:00
|
|
|
json_invalid_key(const char* message) : json_exception(message) {}
|
2019-03-31 01:37:06 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class json_invalid_iterator
|
|
|
|
|
: public json_exception
|
|
|
|
|
{
|
|
|
|
|
public:
|
2019-03-31 23:09:49 +08:00
|
|
|
json_invalid_iterator(const char* message) : json_exception(message) {}
|
2019-03-31 01:37:06 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class json_parse_error
|
|
|
|
|
: public json_exception
|
|
|
|
|
{
|
|
|
|
|
public:
|
2019-03-31 23:09:49 +08:00
|
|
|
json_parse_error(const char* message) : json_exception(message) {}
|
2019-03-31 01:37:06 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace __json_detail
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
// json_value
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
template <typename _BasicJsonTy>
|
|
|
|
|
struct json_value
|
|
|
|
|
{
|
|
|
|
|
using string_type = typename _BasicJsonTy::string_type;
|
|
|
|
|
using char_type = typename _BasicJsonTy::char_type;
|
|
|
|
|
using integer_type = typename _BasicJsonTy::integer_type;
|
|
|
|
|
using float_type = typename _BasicJsonTy::float_type;
|
|
|
|
|
using boolean_type = typename _BasicJsonTy::boolean_type;
|
|
|
|
|
using array_type = typename _BasicJsonTy::array_type;
|
|
|
|
|
using object_type = typename _BasicJsonTy::object_type;
|
|
|
|
|
|
|
|
|
|
JsonType type;
|
|
|
|
|
union
|
|
|
|
|
{
|
|
|
|
|
object_type* object;
|
|
|
|
|
array_type* vector;
|
|
|
|
|
string_type* string;
|
|
|
|
|
integer_type number_integer;
|
|
|
|
|
float_type number_float;
|
|
|
|
|
boolean_type boolean;
|
|
|
|
|
} data;
|
|
|
|
|
|
|
|
|
|
json_value()
|
|
|
|
|
{
|
|
|
|
|
type = JsonType::Null;
|
|
|
|
|
data.object = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_value(std::nullptr_t)
|
|
|
|
|
{
|
|
|
|
|
type = JsonType::Null;
|
|
|
|
|
data.object = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_value(const object_type& value)
|
|
|
|
|
{
|
|
|
|
|
type = JsonType::Object;
|
|
|
|
|
data.object = create<object_type>(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_value(const array_type& value)
|
|
|
|
|
{
|
|
|
|
|
type = JsonType::Array;
|
|
|
|
|
data.vector = create<array_type>(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_value(const string_type& value)
|
|
|
|
|
{
|
|
|
|
|
type = JsonType::String;
|
|
|
|
|
data.string = create<string_type>(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename _CharT>
|
|
|
|
|
json_value(const _CharT* str)
|
|
|
|
|
{
|
|
|
|
|
type = JsonType::String;
|
|
|
|
|
data.string = create<string_type>(str);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_value(const integer_type value)
|
|
|
|
|
{
|
|
|
|
|
type = JsonType::Integer;
|
|
|
|
|
data.number_integer = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_value(const float_type value)
|
|
|
|
|
{
|
|
|
|
|
type = JsonType::Float;
|
|
|
|
|
data.number_float = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_value(const boolean_type value)
|
|
|
|
|
{
|
|
|
|
|
type = JsonType::Boolean;
|
|
|
|
|
data.boolean = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_value(const JsonType value_type)
|
|
|
|
|
{
|
|
|
|
|
type = value_type;
|
|
|
|
|
switch (type)
|
|
|
|
|
{
|
|
|
|
|
case JsonType::Object:
|
|
|
|
|
data.object = create<object_type>();
|
|
|
|
|
break;
|
|
|
|
|
case JsonType::Array:
|
|
|
|
|
data.vector = create<array_type>();
|
|
|
|
|
break;
|
|
|
|
|
case JsonType::String:
|
|
|
|
|
data.string = create<string_type>();
|
|
|
|
|
break;
|
|
|
|
|
case JsonType::Integer:
|
|
|
|
|
data.number_integer = integer_type(0);
|
|
|
|
|
break;
|
|
|
|
|
case JsonType::Float:
|
|
|
|
|
data.number_float = float_type(0.0);
|
|
|
|
|
break;
|
|
|
|
|
case JsonType::Boolean:
|
|
|
|
|
data.boolean = boolean_type(false);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
data.object = nullptr;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_value(json_value const& other)
|
|
|
|
|
{
|
|
|
|
|
type = other.type;
|
|
|
|
|
|
|
|
|
|
switch (other.type)
|
|
|
|
|
{
|
|
|
|
|
case JsonType::Object:
|
|
|
|
|
data.object = create<object_type>(*other.data.object);
|
|
|
|
|
break;
|
|
|
|
|
case JsonType::Array:
|
|
|
|
|
data.vector = create<array_type>(*other.data.vector);
|
|
|
|
|
break;
|
|
|
|
|
case JsonType::String:
|
|
|
|
|
data.string = create<string_type>(*other.data.string);
|
|
|
|
|
break;
|
|
|
|
|
case JsonType::Integer:
|
|
|
|
|
data.number_integer = other.data.number_integer;
|
|
|
|
|
break;
|
|
|
|
|
case JsonType::Float:
|
|
|
|
|
data.number_float = other.data.number_float;
|
|
|
|
|
break;
|
|
|
|
|
case JsonType::Boolean:
|
|
|
|
|
data.boolean = other.data.boolean;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
data.object = nullptr;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_value(json_value&& other)
|
|
|
|
|
{
|
|
|
|
|
type = other.type;
|
|
|
|
|
data = other.data;
|
|
|
|
|
other.type = JsonType::Null;
|
|
|
|
|
other.data.object = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~json_value()
|
|
|
|
|
{
|
|
|
|
|
clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void swap(json_value& other)
|
|
|
|
|
{
|
|
|
|
|
std::swap(type, other.type);
|
|
|
|
|
std::swap(data, other.data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void clear()
|
|
|
|
|
{
|
|
|
|
|
switch (type)
|
|
|
|
|
{
|
|
|
|
|
case JsonType::Object:
|
|
|
|
|
destroy<object_type>(data.object);
|
|
|
|
|
break;
|
|
|
|
|
case JsonType::Array:
|
|
|
|
|
destroy<array_type>(data.vector);
|
|
|
|
|
break;
|
|
|
|
|
case JsonType::String:
|
|
|
|
|
destroy<string_type>(data.string);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename _Ty, typename ..._Args>
|
|
|
|
|
inline _Ty* create(_Args&&... args)
|
|
|
|
|
{
|
|
|
|
|
using allocator_type = typename _BasicJsonTy::template allocator_type<_Ty>;
|
|
|
|
|
using allocator_traits = std::allocator_traits<allocator_type>;
|
|
|
|
|
|
|
|
|
|
allocator_type allocator;
|
|
|
|
|
_Ty* ptr = allocator_traits::allocate(allocator, 1);
|
|
|
|
|
allocator_traits::construct(allocator, ptr, std::forward<_Args>(args)...);
|
|
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename _Ty>
|
|
|
|
|
inline void destroy(_Ty* ptr)
|
|
|
|
|
{
|
|
|
|
|
using allocator_type = typename _BasicJsonTy::template allocator_type<_Ty>;
|
|
|
|
|
using allocator_traits = std::allocator_traits<allocator_type>;
|
|
|
|
|
|
|
|
|
|
allocator_type allocator;
|
|
|
|
|
allocator_traits::destroy(allocator, ptr);
|
|
|
|
|
allocator_traits::deallocate(allocator, ptr, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline json_value& operator=(json_value const& other)
|
|
|
|
|
{
|
|
|
|
|
json_value{ other }.swap(*this);
|
|
|
|
|
return (*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline json_value& operator=(json_value && other)
|
|
|
|
|
{
|
|
|
|
|
clear();
|
|
|
|
|
type = other.type;
|
|
|
|
|
data = std::move(other.data);
|
|
|
|
|
// invalidate payload
|
|
|
|
|
other.type = JsonType::Null;
|
|
|
|
|
other.data.object = nullptr;
|
|
|
|
|
return (*this);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
} // end of namespace __json_detail
|
|
|
|
|
|
|
|
|
|
namespace __json_detail
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
// iterator for basic_json
|
|
|
|
|
//
|
|
|
|
|
|
2019-04-04 14:25:13 +08:00
|
|
|
struct primitive_iterator
|
2019-03-31 01:37:06 +08:00
|
|
|
{
|
|
|
|
|
using difference_type = std::ptrdiff_t;
|
|
|
|
|
|
2019-04-04 14:25:13 +08:00
|
|
|
inline primitive_iterator(int it = 0) : it_(it) {}
|
2019-03-31 01:37:06 +08:00
|
|
|
|
|
|
|
|
inline void set_begin() { it_ = 0; }
|
|
|
|
|
inline void set_end() { it_ = 1; }
|
|
|
|
|
|
2019-04-04 14:25:13 +08:00
|
|
|
inline primitive_iterator& operator++() { ++it_; return *this; }
|
2019-03-31 01:37:06 +08:00
|
|
|
|
2019-04-04 14:25:13 +08:00
|
|
|
inline primitive_iterator operator++(int) { primitive_iterator old(it_); ++(*this); return old; }
|
2019-03-31 01:37:06 +08:00
|
|
|
|
2019-04-04 14:25:13 +08:00
|
|
|
inline primitive_iterator& operator--() { --it_; return (*this); }
|
|
|
|
|
inline primitive_iterator operator--(int) { primitive_iterator old = (*this); --(*this); return old; }
|
2019-03-31 01:37:06 +08:00
|
|
|
|
2019-04-04 14:25:13 +08:00
|
|
|
inline bool operator==(primitive_iterator const& other) const { return it_ == other.it_; }
|
|
|
|
|
inline bool operator!=(primitive_iterator const& other) const { return !(*this == other); }
|
2019-03-31 01:37:06 +08:00
|
|
|
|
2019-04-04 14:25:13 +08:00
|
|
|
inline const primitive_iterator operator+(difference_type off) const { return primitive_iterator(it_ + off); }
|
|
|
|
|
inline const primitive_iterator operator-(difference_type off) const { return primitive_iterator(it_ - off); }
|
2019-03-31 01:37:06 +08:00
|
|
|
|
2019-04-04 14:25:13 +08:00
|
|
|
inline primitive_iterator& operator+=(difference_type off) { it_ += off; return (*this); }
|
|
|
|
|
inline primitive_iterator& operator-=(difference_type off) { it_ -= off; return (*this); }
|
2019-03-31 01:37:06 +08:00
|
|
|
|
2019-04-04 14:25:13 +08:00
|
|
|
inline difference_type operator-(primitive_iterator const& other) const { return it_ - other.it_; }
|
2019-03-31 01:37:06 +08:00
|
|
|
|
2019-04-04 14:25:13 +08:00
|
|
|
inline bool operator<(primitive_iterator const& other) const { return it_ < other.it_; }
|
|
|
|
|
inline bool operator<=(primitive_iterator const& other) const { return it_ <= other.it_; }
|
|
|
|
|
inline bool operator>(primitive_iterator const& other) const { return it_ > other.it_; }
|
|
|
|
|
inline bool operator>=(primitive_iterator const& other) const { return it_ >= other.it_; }
|
2019-03-31 01:37:06 +08:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
int it_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename _BasicJsonTy>
|
|
|
|
|
struct internal_iterator
|
|
|
|
|
{
|
|
|
|
|
typename _BasicJsonTy::array_type::iterator array_iter;
|
|
|
|
|
typename _BasicJsonTy::object_type::iterator object_iter;
|
2019-04-04 14:25:13 +08:00
|
|
|
primitive_iterator original_iter = 0; // for other types
|
2019-03-31 01:37:06 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename _BasicJsonTy>
|
|
|
|
|
struct iterator_impl
|
|
|
|
|
{
|
|
|
|
|
friend _BasicJsonTy;
|
|
|
|
|
|
|
|
|
|
using string_type = typename _BasicJsonTy::string_type;
|
|
|
|
|
using char_type = typename _BasicJsonTy::char_type;
|
|
|
|
|
using integer_type = typename _BasicJsonTy::integer_type;
|
|
|
|
|
using float_type = typename _BasicJsonTy::float_type;
|
|
|
|
|
using boolean_type = typename _BasicJsonTy::boolean_type;
|
|
|
|
|
using array_type = typename _BasicJsonTy::array_type;
|
|
|
|
|
using object_type = typename _BasicJsonTy::object_type;
|
|
|
|
|
|
|
|
|
|
using value_type = _BasicJsonTy;
|
|
|
|
|
using difference_type = std::ptrdiff_t;
|
|
|
|
|
using iterator_category = std::bidirectional_iterator_tag;
|
|
|
|
|
using pointer = value_type*;
|
|
|
|
|
using reference = value_type&;
|
|
|
|
|
|
|
|
|
|
inline iterator_impl(pointer json = nullptr) : data_(json) {}
|
|
|
|
|
|
|
|
|
|
inline reference operator*() const
|
|
|
|
|
{
|
|
|
|
|
check_data();
|
|
|
|
|
check_iterator();
|
|
|
|
|
switch (data_->type())
|
|
|
|
|
{
|
|
|
|
|
case JsonType::Object:
|
|
|
|
|
return (it_.object_iter->second);
|
|
|
|
|
case JsonType::Array:
|
|
|
|
|
return (*it_.array_iter);
|
|
|
|
|
default:
|
|
|
|
|
return *data_;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline pointer operator->() const
|
|
|
|
|
{
|
|
|
|
|
check_data();
|
|
|
|
|
check_iterator();
|
|
|
|
|
switch (data_->type())
|
|
|
|
|
{
|
|
|
|
|
case JsonType::Object:
|
|
|
|
|
return &(it_.object_iter->second);
|
|
|
|
|
case JsonType::Array:
|
|
|
|
|
return &(*it_.array_iter);
|
|
|
|
|
default:
|
|
|
|
|
return data_;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline const typename object_type::key_type& key() const
|
|
|
|
|
{
|
|
|
|
|
check_data();
|
|
|
|
|
check_iterator();
|
|
|
|
|
if (!data_->is_object())
|
2019-03-31 23:09:49 +08:00
|
|
|
throw json_invalid_iterator("cannot use key() with non-object type");
|
2019-03-31 01:37:06 +08:00
|
|
|
return it_.object_iter->first;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline reference value() const
|
|
|
|
|
{
|
|
|
|
|
return operator*();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void set_begin()
|
|
|
|
|
{
|
|
|
|
|
check_data();
|
|
|
|
|
|
|
|
|
|
switch (data_->type())
|
|
|
|
|
{
|
|
|
|
|
case JsonType::Object:
|
|
|
|
|
{
|
|
|
|
|
it_.object_iter = data_->value_.data.object->begin();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case JsonType::Array:
|
|
|
|
|
{
|
|
|
|
|
it_.array_iter = data_->value_.data.vector->begin();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
it_.original_iter.set_begin();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void set_end()
|
|
|
|
|
{
|
|
|
|
|
check_data();
|
|
|
|
|
|
|
|
|
|
switch (data_->type())
|
|
|
|
|
{
|
|
|
|
|
case JsonType::Object:
|
|
|
|
|
{
|
|
|
|
|
it_.object_iter = data_->value_.data.object->end();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case JsonType::Array:
|
|
|
|
|
{
|
|
|
|
|
it_.array_iter = data_->value_.data.vector->end();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
it_.original_iter.set_end();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline iterator_impl operator++(int) { iterator_impl old = (*this); ++(*this); return old; }
|
|
|
|
|
inline iterator_impl& operator++()
|
|
|
|
|
{
|
|
|
|
|
check_data();
|
|
|
|
|
|
|
|
|
|
switch (data_->type())
|
|
|
|
|
{
|
|
|
|
|
case JsonType::Object:
|
|
|
|
|
{
|
|
|
|
|
std::advance(it_.object_iter, 1);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case JsonType::Array:
|
|
|
|
|
{
|
|
|
|
|
std::advance(it_.array_iter, 1);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
++it_.original_iter;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline iterator_impl operator--(int) { iterator_impl old = (*this); --(*this); return old; }
|
|
|
|
|
inline iterator_impl& operator--()
|
|
|
|
|
{
|
|
|
|
|
check_data();
|
|
|
|
|
|
|
|
|
|
switch (data_->type())
|
|
|
|
|
{
|
|
|
|
|
case JsonType::Object:
|
|
|
|
|
{
|
|
|
|
|
std::advance(it_.object_iter, -1);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case JsonType::Array:
|
|
|
|
|
{
|
|
|
|
|
std::advance(it_.array_iter, -1);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
--it_.original_iter;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline const iterator_impl operator-(difference_type off) const { return operator+(-off); }
|
|
|
|
|
inline const iterator_impl operator+(difference_type off) const { iterator_impl ret(*this); ret += off; return ret; }
|
|
|
|
|
|
|
|
|
|
inline iterator_impl& operator-=(difference_type off) { return operator+=(-off); }
|
|
|
|
|
inline iterator_impl& operator+=(difference_type off)
|
|
|
|
|
{
|
|
|
|
|
check_data();
|
|
|
|
|
|
|
|
|
|
switch (data_->type())
|
|
|
|
|
{
|
|
|
|
|
case JsonType::Object:
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
throw json_invalid_iterator("cannot use offsets with object type");
|
2019-03-31 01:37:06 +08:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case JsonType::Array:
|
|
|
|
|
{
|
|
|
|
|
std::advance(it_.array_iter, off);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
it_.original_iter += off;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool operator!=(iterator_impl const& other) const { return !(*this == other); }
|
|
|
|
|
inline bool operator==(iterator_impl const& other) const
|
|
|
|
|
{
|
|
|
|
|
check_data();
|
|
|
|
|
other.check_data();
|
|
|
|
|
|
2019-03-31 23:09:49 +08:00
|
|
|
if (data_ != other.data_)
|
|
|
|
|
throw json_invalid_iterator("cannot compare iterators of different objects");
|
2019-03-31 01:37:06 +08:00
|
|
|
|
|
|
|
|
switch (data_->type())
|
|
|
|
|
{
|
|
|
|
|
case JsonType::Object:
|
|
|
|
|
{
|
|
|
|
|
return it_.object_iter == other.it_.object_iter;
|
|
|
|
|
}
|
|
|
|
|
case JsonType::Array:
|
|
|
|
|
{
|
|
|
|
|
return it_.array_iter == other.it_.array_iter;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
return it_.original_iter == other.it_.original_iter;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool operator>(iterator_impl const& other) const { return other.operator<(*this); }
|
|
|
|
|
inline bool operator>=(iterator_impl const& other) const { return !operator<(other); }
|
|
|
|
|
inline bool operator<=(iterator_impl const& other) const { return !other.operator<(*this); }
|
|
|
|
|
inline bool operator<(iterator_impl const& other) const
|
|
|
|
|
{
|
|
|
|
|
check_data();
|
2019-03-31 23:09:49 +08:00
|
|
|
other.check_data();
|
|
|
|
|
|
|
|
|
|
if (data_ != other.data_)
|
|
|
|
|
throw json_invalid_iterator("cannot compare iterators of different objects");
|
2019-03-31 01:37:06 +08:00
|
|
|
|
|
|
|
|
switch (data_->type())
|
|
|
|
|
{
|
|
|
|
|
case JsonType::Object:
|
2019-03-31 23:09:49 +08:00
|
|
|
throw json_invalid_iterator("cannot compare iterators with object type");
|
2019-03-31 01:37:06 +08:00
|
|
|
case JsonType::Array:
|
|
|
|
|
return it_.array_iter < other.it_.array_iter;
|
|
|
|
|
default:
|
|
|
|
|
return it_.original_iter < other.it_.original_iter;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
inline void check_data() const
|
|
|
|
|
{
|
|
|
|
|
if (data_ == nullptr)
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
throw json_invalid_iterator("iterator contains an empty object");
|
2019-03-31 01:37:06 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void check_iterator() const
|
|
|
|
|
{
|
|
|
|
|
switch (data_->type())
|
|
|
|
|
{
|
|
|
|
|
case JsonType::Object:
|
|
|
|
|
if (it_.object_iter == data_->value_.data.object->end())
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
throw std::out_of_range("iterator out of range");
|
2019-03-31 01:37:06 +08:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case JsonType::Array:
|
|
|
|
|
if (it_.array_iter == data_->value_.data.vector->end())
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
throw std::out_of_range("iterator out of range");
|
2019-03-31 01:37:06 +08:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
if (it_.original_iter == 1)
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
throw std::out_of_range("iterator out of range");
|
2019-03-31 01:37:06 +08:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
pointer data_;
|
|
|
|
|
internal_iterator<_BasicJsonTy> it_;
|
|
|
|
|
};
|
|
|
|
|
} // end of namespace __json_detail
|
|
|
|
|
|
|
|
|
|
namespace __json_detail
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
// output_adapter
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
template <typename _CharTy>
|
|
|
|
|
struct output_adapter
|
|
|
|
|
{
|
|
|
|
|
using char_type = _CharTy;
|
|
|
|
|
using char_traits = std::char_traits<char_type>;
|
|
|
|
|
|
|
|
|
|
virtual void write(const _CharTy ch) = 0;
|
|
|
|
|
virtual void write(const _CharTy* str, std::size_t size) = 0;
|
|
|
|
|
virtual void write(const _CharTy* str)
|
|
|
|
|
{
|
|
|
|
|
const auto size = char_traits::length(str);
|
|
|
|
|
write(str, static_cast<std::size_t>(size));
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename _StringTy>
|
|
|
|
|
struct string_output_adapter
|
|
|
|
|
: public output_adapter<typename _StringTy::value_type>
|
|
|
|
|
{
|
|
|
|
|
using char_type = typename _StringTy::value_type;
|
|
|
|
|
using size_type = typename _StringTy::size_type;
|
|
|
|
|
using char_traits = std::char_traits<char_type>;
|
|
|
|
|
|
|
|
|
|
string_output_adapter(_StringTy& str) : str_(str) {}
|
|
|
|
|
|
|
|
|
|
virtual void write(const char_type ch) override
|
|
|
|
|
{
|
|
|
|
|
str_.push_back(ch);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual void write(const char_type* str, std::size_t size) override
|
|
|
|
|
{
|
|
|
|
|
str_.append(str, static_cast<size_type>(size));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
_StringTy& str_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename _CharTy>
|
|
|
|
|
struct stream_output_adapter
|
|
|
|
|
: public output_adapter<_CharTy>
|
|
|
|
|
{
|
|
|
|
|
using char_type = _CharTy;
|
|
|
|
|
using size_type = typename std::streamsize;
|
|
|
|
|
using char_traits = std::char_traits<char_type>;
|
|
|
|
|
|
|
|
|
|
stream_output_adapter(std::basic_ostream<char_type>& stream) : stream_(stream) {}
|
|
|
|
|
|
|
|
|
|
virtual void write(const char_type ch) override
|
|
|
|
|
{
|
|
|
|
|
stream_.put(ch);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual void write(const char_type* str, std::size_t size) override
|
|
|
|
|
{
|
|
|
|
|
stream_.write(str, static_cast<size_type>(size));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
std::basic_ostream<char_type>& stream_;
|
|
|
|
|
};
|
|
|
|
|
} // end of namespace __json_detail
|
|
|
|
|
|
|
|
|
|
namespace __json_detail
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
// json_serializer
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
template <typename _BasicJsonTy>
|
|
|
|
|
struct json_serializer
|
|
|
|
|
{
|
|
|
|
|
using string_type = typename _BasicJsonTy::string_type;
|
|
|
|
|
using char_type = typename _BasicJsonTy::char_type;
|
|
|
|
|
using integer_type = typename _BasicJsonTy::integer_type;
|
|
|
|
|
using float_type = typename _BasicJsonTy::float_type;
|
|
|
|
|
using boolean_type = typename _BasicJsonTy::boolean_type;
|
|
|
|
|
using array_type = typename _BasicJsonTy::array_type;
|
|
|
|
|
using object_type = typename _BasicJsonTy::object_type;
|
|
|
|
|
|
|
|
|
|
json_serializer(output_adapter<char_type>* out, const wchar_t indent_char)
|
|
|
|
|
: out(out)
|
|
|
|
|
, indent_char(indent_char)
|
|
|
|
|
, indent_string(32, indent_char)
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
void dump(
|
|
|
|
|
const _BasicJsonTy& json,
|
|
|
|
|
const bool pretty_print,
|
|
|
|
|
const unsigned int indent_step,
|
|
|
|
|
const unsigned int current_indent = 0)
|
|
|
|
|
{
|
|
|
|
|
switch (json.type())
|
|
|
|
|
{
|
|
|
|
|
case JsonType::Object:
|
|
|
|
|
{
|
|
|
|
|
auto& object = *json.value_.data.object;
|
|
|
|
|
|
|
|
|
|
if (object.empty())
|
|
|
|
|
{
|
|
|
|
|
out->write(L"{}");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pretty_print)
|
|
|
|
|
{
|
|
|
|
|
out->write(L"{\n");
|
|
|
|
|
|
|
|
|
|
const auto new_indent = current_indent + indent_step;
|
|
|
|
|
if (indent_string.size() < new_indent)
|
|
|
|
|
{
|
|
|
|
|
indent_string.resize(indent_string.size() * 2, indent_char);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto iter = object.cbegin();
|
|
|
|
|
const auto size = object.size();
|
|
|
|
|
for (std::size_t i = 0; i < size; ++i, ++iter)
|
|
|
|
|
{
|
|
|
|
|
out->write(indent_string.c_str(), new_indent);
|
|
|
|
|
out->write('\"');
|
|
|
|
|
out->write(iter->first.c_str());
|
|
|
|
|
out->write(L"\": ");
|
|
|
|
|
dump(iter->second, true, indent_step, new_indent);
|
|
|
|
|
|
|
|
|
|
// not last element
|
|
|
|
|
if (i != size - 1)
|
|
|
|
|
out->write(L",\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out->write('\n');
|
|
|
|
|
out->write(indent_string.c_str(), current_indent);
|
|
|
|
|
out->write('}');
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
out->write('{');
|
|
|
|
|
|
|
|
|
|
auto iter = object.cbegin();
|
|
|
|
|
const auto size = object.size();
|
|
|
|
|
for (std::size_t i = 0; i < size; ++i, ++iter)
|
|
|
|
|
{
|
|
|
|
|
out->write('\"');
|
|
|
|
|
out->write(iter->first.c_str());
|
|
|
|
|
out->write(L"\":");
|
|
|
|
|
dump(iter->second, false, indent_step, current_indent);
|
|
|
|
|
|
|
|
|
|
// not last element
|
|
|
|
|
if (i != size - 1)
|
|
|
|
|
out->write(',');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out->write('}');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case JsonType::Array:
|
|
|
|
|
{
|
|
|
|
|
auto& vector = *json.value_.data.vector;
|
|
|
|
|
|
|
|
|
|
if (vector.empty())
|
|
|
|
|
{
|
|
|
|
|
out->write(L"[]");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pretty_print)
|
|
|
|
|
{
|
|
|
|
|
out->write(L"[\n");
|
|
|
|
|
|
|
|
|
|
const auto new_indent = current_indent + indent_step;
|
|
|
|
|
if (indent_string.size() < new_indent)
|
|
|
|
|
{
|
|
|
|
|
indent_string.resize(indent_string.size() * 2, indent_char);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto iter = vector.cbegin();
|
|
|
|
|
const auto size = vector.size();
|
|
|
|
|
for (std::size_t i = 0; i < size; ++i, ++iter)
|
|
|
|
|
{
|
|
|
|
|
out->write(indent_string.c_str(), new_indent);
|
|
|
|
|
dump(*iter, true, indent_step, new_indent);
|
|
|
|
|
|
|
|
|
|
// not last element
|
|
|
|
|
if (i != size - 1)
|
|
|
|
|
out->write(L",\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out->write('\n');
|
|
|
|
|
out->write(indent_string.c_str(), current_indent);
|
|
|
|
|
out->write(']');
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
out->write('[');
|
|
|
|
|
|
|
|
|
|
auto iter = vector.cbegin();
|
|
|
|
|
const auto size = vector.size();
|
|
|
|
|
for (std::size_t i = 0; i < size; ++i, ++iter)
|
|
|
|
|
{
|
|
|
|
|
dump(*iter, false, indent_step, current_indent);
|
|
|
|
|
// not last element
|
|
|
|
|
if (i != size - 1)
|
|
|
|
|
out->write(',');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out->write(']');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case JsonType::String:
|
|
|
|
|
{
|
|
|
|
|
out->write('\"');
|
2019-03-31 13:56:05 +08:00
|
|
|
dump_string(*json.value_.data.string);
|
2019-03-31 01:37:06 +08:00
|
|
|
out->write('\"');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case JsonType::Boolean:
|
|
|
|
|
{
|
|
|
|
|
if (json.value_.data.boolean)
|
|
|
|
|
{
|
|
|
|
|
out->write(L"true");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
out->write(L"false");
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case JsonType::Integer:
|
|
|
|
|
{
|
|
|
|
|
dump_integer(json.value_.data.number_integer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case JsonType::Float:
|
|
|
|
|
{
|
|
|
|
|
dump_float(json.value_.data.number_float);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case JsonType::Null:
|
|
|
|
|
{
|
|
|
|
|
out->write(L"null");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void dump_integer(integer_type val)
|
|
|
|
|
{
|
|
|
|
|
if (val == 0)
|
|
|
|
|
{
|
|
|
|
|
out->write('0');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto uval = static_cast<std::make_unsigned_t<integer_type>>(val);
|
|
|
|
|
|
|
|
|
|
if (val < 0)
|
|
|
|
|
uval = 0 - uval;
|
|
|
|
|
|
|
|
|
|
auto next = number_buffer.rbegin();
|
|
|
|
|
*next = '\0';
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
*(++next) = static_cast<wchar_t>('0' + uval % 10);
|
|
|
|
|
uval /= 10;
|
|
|
|
|
} while (uval != 0);
|
|
|
|
|
|
|
|
|
|
if (val < 0)
|
|
|
|
|
*(++next) = '-';
|
|
|
|
|
|
|
|
|
|
out->write(&(*next));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void dump_float(float_type val)
|
|
|
|
|
{
|
|
|
|
|
const auto digits = std::numeric_limits<float_type>::max_digits10;
|
|
|
|
|
const auto len = ::_scwprintf(L"%.*g", digits, val);
|
|
|
|
|
if (len)
|
|
|
|
|
{
|
|
|
|
|
number_buffer[0] = '\0';
|
|
|
|
|
::swprintf_s(&number_buffer[0], len + 1, L"%.*g", digits, val);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
number_buffer[0] = '0';
|
|
|
|
|
number_buffer[1] = '.';
|
|
|
|
|
number_buffer[2] = '0';
|
|
|
|
|
number_buffer[3] = '\0';
|
|
|
|
|
}
|
|
|
|
|
out->write(number_buffer.data());
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-31 13:56:05 +08:00
|
|
|
void dump_string(const string_type & val)
|
|
|
|
|
{
|
|
|
|
|
for (const auto& ch : val)
|
|
|
|
|
{
|
|
|
|
|
switch (ch)
|
|
|
|
|
{
|
|
|
|
|
case '\t':
|
2019-03-31 23:09:49 +08:00
|
|
|
{
|
|
|
|
|
out->write(L"\\t");
|
2019-03-31 13:56:05 +08:00
|
|
|
break;
|
2019-03-31 23:09:49 +08:00
|
|
|
}
|
|
|
|
|
|
2019-03-31 13:56:05 +08:00
|
|
|
case '\r':
|
2019-03-31 23:09:49 +08:00
|
|
|
{
|
|
|
|
|
out->write(L"\\r");
|
2019-03-31 13:56:05 +08:00
|
|
|
break;
|
2019-03-31 23:09:49 +08:00
|
|
|
}
|
|
|
|
|
|
2019-03-31 13:56:05 +08:00
|
|
|
case '\n':
|
2019-03-31 23:09:49 +08:00
|
|
|
{
|
|
|
|
|
out->write(L"\\n");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case '\b':
|
|
|
|
|
{
|
|
|
|
|
out->write(L"\\b");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case '\f':
|
|
|
|
|
{
|
|
|
|
|
out->write(L"\\f");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case '\"':
|
|
|
|
|
{
|
|
|
|
|
out->write(L"\\\"");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case '\\':
|
|
|
|
|
{
|
|
|
|
|
out->write(L"\\\\");
|
2019-03-31 13:56:05 +08:00
|
|
|
break;
|
2019-03-31 23:09:49 +08:00
|
|
|
}
|
|
|
|
|
|
2019-03-31 13:56:05 +08:00
|
|
|
default:
|
2019-03-31 23:09:49 +08:00
|
|
|
{
|
|
|
|
|
const auto char_byte = static_cast<uint16_t>(ch);
|
|
|
|
|
if ((char_byte > 0x1F) && (char_byte < 0x7F))
|
|
|
|
|
{
|
|
|
|
|
out->write(ch);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
wchar_t escaped[7] = { 0 };
|
|
|
|
|
::swprintf_s(escaped, 7, L"\\u%04x", char_byte);
|
|
|
|
|
out->write(escaped);
|
|
|
|
|
}
|
2019-03-31 13:56:05 +08:00
|
|
|
break;
|
|
|
|
|
}
|
2019-03-31 23:09:49 +08:00
|
|
|
}
|
2019-03-31 13:56:05 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-31 01:37:06 +08:00
|
|
|
private:
|
|
|
|
|
output_adapter<char_type>* out;
|
|
|
|
|
char_type indent_char;
|
|
|
|
|
string_type indent_string;
|
|
|
|
|
std::array<char_type, 21> number_buffer;
|
|
|
|
|
};
|
|
|
|
|
} // end of namespace __json_detail
|
|
|
|
|
|
|
|
|
|
namespace __json_detail
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
// input_adapter
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
template <typename _CharTy>
|
|
|
|
|
struct input_adapter
|
|
|
|
|
{
|
|
|
|
|
using char_type = _CharTy;
|
|
|
|
|
using char_traits = std::char_traits<char_type>;
|
|
|
|
|
|
|
|
|
|
virtual typename char_traits::int_type get_char() = 0;
|
|
|
|
|
virtual ~input_adapter() = default;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename _CharTy>
|
|
|
|
|
struct file_input_adapter
|
|
|
|
|
: public input_adapter<_CharTy>
|
|
|
|
|
{
|
|
|
|
|
using char_type = typename input_adapter<_CharTy>::char_type;
|
|
|
|
|
using char_traits = typename input_adapter<_CharTy>::char_traits;
|
|
|
|
|
|
|
|
|
|
file_input_adapter(std::FILE* file) : file(file) {}
|
|
|
|
|
|
|
|
|
|
virtual typename char_traits::int_type get_char() override
|
|
|
|
|
{
|
|
|
|
|
return std::fgetc(file);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
std::FILE* file;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename _CharTy>
|
|
|
|
|
struct stream_input_adapter
|
|
|
|
|
: public input_adapter<_CharTy>
|
|
|
|
|
{
|
|
|
|
|
using char_type = typename input_adapter<_CharTy>::char_type;
|
|
|
|
|
using char_traits = typename input_adapter<_CharTy>::char_traits;
|
|
|
|
|
|
|
|
|
|
stream_input_adapter(std::basic_istream<char_type>& stream) : stream(stream), streambuf(*stream.rdbuf()) {}
|
|
|
|
|
|
|
|
|
|
virtual typename char_traits::int_type get_char() override
|
|
|
|
|
{
|
|
|
|
|
auto ch = streambuf.sbumpc();
|
|
|
|
|
if (ch == EOF)
|
|
|
|
|
{
|
|
|
|
|
stream.clear(stream.rdstate() | std::ios::eofbit);
|
|
|
|
|
}
|
|
|
|
|
return ch;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual ~stream_input_adapter()
|
|
|
|
|
{
|
|
|
|
|
stream.clear(stream.rdstate() & std::ios::eofbit);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
std::basic_istream<char_type>& stream;
|
|
|
|
|
std::basic_streambuf<char_type>& streambuf;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename _StringTy>
|
|
|
|
|
struct string_input_adapter
|
|
|
|
|
: public input_adapter<typename _StringTy::value_type>
|
|
|
|
|
{
|
|
|
|
|
using char_type = typename input_adapter<typename _StringTy::value_type>::char_type;
|
|
|
|
|
using char_traits = typename input_adapter<typename _StringTy::value_type>::char_traits;
|
|
|
|
|
|
|
|
|
|
string_input_adapter(const _StringTy& str) : str(str), index(0) {}
|
|
|
|
|
|
|
|
|
|
virtual typename char_traits::int_type get_char() override
|
|
|
|
|
{
|
|
|
|
|
if (index == str.size())
|
|
|
|
|
return char_traits::eof();
|
|
|
|
|
return str[index++];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
const _StringTy& str;
|
|
|
|
|
typename _StringTy::size_type index;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename _CharTy>
|
|
|
|
|
struct buffer_input_adapter
|
|
|
|
|
: public input_adapter<_CharTy>
|
|
|
|
|
{
|
|
|
|
|
using char_type = typename input_adapter<_CharTy>::char_type;
|
|
|
|
|
using char_traits = typename input_adapter<_CharTy>::char_traits;
|
|
|
|
|
|
|
|
|
|
buffer_input_adapter(const _CharTy* str) : str(str), index(0) {}
|
|
|
|
|
|
|
|
|
|
virtual typename char_traits::int_type get_char() override
|
|
|
|
|
{
|
|
|
|
|
if (str[index] == '\0')
|
|
|
|
|
return char_traits::eof();
|
|
|
|
|
return str[index++];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
const char_type* str;
|
|
|
|
|
std::size_t index;
|
|
|
|
|
};
|
|
|
|
|
} // end of namespace __json_detail
|
|
|
|
|
|
|
|
|
|
namespace __json_detail
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
// json_lexer & json_parser
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
enum class token_type
|
|
|
|
|
{
|
|
|
|
|
uninitialized,
|
|
|
|
|
|
|
|
|
|
literal_true,
|
|
|
|
|
literal_false,
|
|
|
|
|
literal_null,
|
|
|
|
|
|
|
|
|
|
value_string,
|
|
|
|
|
value_integer,
|
|
|
|
|
value_float,
|
|
|
|
|
|
|
|
|
|
begin_array,
|
|
|
|
|
end_array,
|
|
|
|
|
|
|
|
|
|
begin_object,
|
|
|
|
|
end_object,
|
|
|
|
|
|
|
|
|
|
name_separator,
|
|
|
|
|
value_separator,
|
|
|
|
|
|
|
|
|
|
parse_error,
|
|
|
|
|
|
|
|
|
|
end_of_input
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename _BasicJsonTy>
|
|
|
|
|
struct json_lexer
|
|
|
|
|
{
|
|
|
|
|
using string_type = typename _BasicJsonTy::string_type;
|
|
|
|
|
using char_type = typename _BasicJsonTy::char_type;
|
|
|
|
|
using integer_type = typename _BasicJsonTy::integer_type;
|
|
|
|
|
using float_type = typename _BasicJsonTy::float_type;
|
|
|
|
|
using boolean_type = typename _BasicJsonTy::boolean_type;
|
|
|
|
|
using array_type = typename _BasicJsonTy::array_type;
|
|
|
|
|
using object_type = typename _BasicJsonTy::object_type;
|
|
|
|
|
using char_traits = std::char_traits<char_type>;
|
|
|
|
|
|
|
|
|
|
json_lexer(input_adapter<char_type>* adapter) : adapter(adapter)
|
|
|
|
|
{
|
|
|
|
|
// read first char
|
|
|
|
|
read_next();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typename char_traits::int_type read_next()
|
|
|
|
|
{
|
|
|
|
|
current = adapter->get_char();
|
|
|
|
|
return current;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void skip_spaces()
|
|
|
|
|
{
|
|
|
|
|
while (current == ' ' || current == '\t' || current == '\n' || current == '\r')
|
|
|
|
|
{
|
|
|
|
|
read_next();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
token_type scan()
|
|
|
|
|
{
|
|
|
|
|
skip_spaces();
|
|
|
|
|
|
|
|
|
|
token_type result = token_type::uninitialized;
|
|
|
|
|
switch (current)
|
|
|
|
|
{
|
|
|
|
|
case '[':
|
|
|
|
|
result = token_type::begin_array;
|
|
|
|
|
break;
|
|
|
|
|
case ']':
|
|
|
|
|
result = token_type::end_array;
|
|
|
|
|
break;
|
|
|
|
|
case '{':
|
|
|
|
|
result = token_type::begin_object;
|
|
|
|
|
break;
|
|
|
|
|
case '}':
|
|
|
|
|
result = token_type::end_object;
|
|
|
|
|
break;
|
|
|
|
|
case ':':
|
|
|
|
|
result = token_type::name_separator;
|
|
|
|
|
break;
|
|
|
|
|
case ',':
|
|
|
|
|
result = token_type::value_separator;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 't':
|
|
|
|
|
return scan_literal(L"true", token_type::literal_true);
|
|
|
|
|
case 'f':
|
|
|
|
|
return scan_literal(L"false", token_type::literal_false);
|
|
|
|
|
case 'n':
|
|
|
|
|
return scan_literal(L"null", token_type::literal_null);
|
|
|
|
|
|
|
|
|
|
case '\"':
|
|
|
|
|
return scan_string();
|
|
|
|
|
|
|
|
|
|
case '-':
|
|
|
|
|
case '0':
|
|
|
|
|
case '1':
|
|
|
|
|
case '2':
|
|
|
|
|
case '3':
|
|
|
|
|
case '4':
|
|
|
|
|
case '5':
|
|
|
|
|
case '6':
|
|
|
|
|
case '7':
|
|
|
|
|
case '8':
|
|
|
|
|
case '9':
|
|
|
|
|
return scan_number();
|
|
|
|
|
|
|
|
|
|
case '\0':
|
|
|
|
|
case char_traits::eof():
|
|
|
|
|
return token_type::end_of_input;
|
|
|
|
|
|
|
|
|
|
// unexpected char
|
|
|
|
|
default:
|
|
|
|
|
return token_type::parse_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// skip current char
|
|
|
|
|
read_next();
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
token_type scan_literal(const char_type* text, token_type result)
|
|
|
|
|
{
|
|
|
|
|
for (std::size_t i = 0; text[i] != '\0'; ++i)
|
|
|
|
|
{
|
|
|
|
|
if (text[i] != char_traits::to_char_type(current))
|
|
|
|
|
{
|
|
|
|
|
return token_type::parse_error;
|
|
|
|
|
}
|
|
|
|
|
read_next();
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
token_type scan_string()
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
if (current != '\"')
|
|
|
|
|
return token_type::parse_error;
|
|
|
|
|
|
|
|
|
|
string_buffer.clear();
|
|
|
|
|
|
|
|
|
|
while (true)
|
2019-03-31 01:37:06 +08:00
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
const auto ch = read_next();
|
|
|
|
|
switch (ch)
|
|
|
|
|
{
|
|
|
|
|
case char_traits::eof():
|
|
|
|
|
{
|
|
|
|
|
// unexpected end
|
|
|
|
|
return token_type::parse_error;
|
|
|
|
|
}
|
2019-03-31 01:37:06 +08:00
|
|
|
|
2019-03-31 23:09:49 +08:00
|
|
|
case '\"':
|
2019-03-31 01:37:06 +08:00
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
// skip last `\"`
|
|
|
|
|
read_next();
|
|
|
|
|
return token_type::value_string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case 0x00:
|
|
|
|
|
case 0x01:
|
|
|
|
|
case 0x02:
|
|
|
|
|
case 0x03:
|
|
|
|
|
case 0x04:
|
|
|
|
|
case 0x05:
|
|
|
|
|
case 0x06:
|
|
|
|
|
case 0x07:
|
|
|
|
|
case 0x08:
|
|
|
|
|
case 0x09:
|
|
|
|
|
case 0x0A:
|
|
|
|
|
case 0x0B:
|
|
|
|
|
case 0x0C:
|
|
|
|
|
case 0x0D:
|
|
|
|
|
case 0x0E:
|
|
|
|
|
case 0x0F:
|
|
|
|
|
case 0x10:
|
|
|
|
|
case 0x11:
|
|
|
|
|
case 0x12:
|
|
|
|
|
case 0x13:
|
|
|
|
|
case 0x14:
|
|
|
|
|
case 0x15:
|
|
|
|
|
case 0x16:
|
|
|
|
|
case 0x17:
|
|
|
|
|
case 0x18:
|
|
|
|
|
case 0x19:
|
|
|
|
|
case 0x1A:
|
|
|
|
|
case 0x1B:
|
|
|
|
|
case 0x1C:
|
|
|
|
|
case 0x1D:
|
|
|
|
|
case 0x1E:
|
|
|
|
|
case 0x1F:
|
|
|
|
|
{
|
|
|
|
|
// invalid control character
|
|
|
|
|
return token_type::parse_error;
|
|
|
|
|
}
|
2019-03-31 13:56:05 +08:00
|
|
|
|
2019-03-31 23:09:49 +08:00
|
|
|
case '\\':
|
|
|
|
|
{
|
|
|
|
|
switch (read_next())
|
2019-03-31 13:56:05 +08:00
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
case '\"':
|
|
|
|
|
string_buffer.push_back('\"');
|
|
|
|
|
break;
|
|
|
|
|
case '\\':
|
|
|
|
|
string_buffer.push_back('\\');
|
|
|
|
|
break;
|
|
|
|
|
case '/':
|
|
|
|
|
string_buffer.push_back('/');
|
|
|
|
|
break;
|
|
|
|
|
case 'b':
|
|
|
|
|
string_buffer.push_back('\b');
|
|
|
|
|
break;
|
|
|
|
|
case 'f':
|
|
|
|
|
string_buffer.push_back('\f');
|
|
|
|
|
break;
|
|
|
|
|
case 'n':
|
|
|
|
|
string_buffer.push_back('\n');
|
|
|
|
|
break;
|
|
|
|
|
case 'r':
|
|
|
|
|
string_buffer.push_back('\r');
|
|
|
|
|
break;
|
|
|
|
|
case 't':
|
|
|
|
|
string_buffer.push_back('\t');
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'u':
|
|
|
|
|
{
|
|
|
|
|
// unicode escapes
|
|
|
|
|
uint16_t byte = 0;
|
|
|
|
|
|
|
|
|
|
for (const auto factor : { 12, 8, 4, 0 })
|
|
|
|
|
{
|
|
|
|
|
const auto n = read_next();
|
|
|
|
|
if (n >= L'0' && n <= L'9')
|
|
|
|
|
{
|
|
|
|
|
byte += ((n - L'0') << factor);
|
|
|
|
|
}
|
|
|
|
|
else if (n >= L'A' && n <= L'F')
|
|
|
|
|
{
|
|
|
|
|
byte += ((n - L'A' + 10) << factor);
|
|
|
|
|
}
|
|
|
|
|
else if (n >= L'a' && n <= L'f')
|
|
|
|
|
{
|
|
|
|
|
byte += ((n - L'a' + 10) << factor);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// '\u' must be followed by 4 hex digits
|
|
|
|
|
return token_type::parse_error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string_buffer.push_back(char_traits::to_char_type(byte));
|
|
|
|
|
break;
|
2019-03-31 13:56:05 +08:00
|
|
|
}
|
2019-03-31 23:09:49 +08:00
|
|
|
|
|
|
|
|
default:
|
2019-03-31 13:56:05 +08:00
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
return token_type::parse_error;
|
|
|
|
|
}
|
2019-03-31 13:56:05 +08:00
|
|
|
}
|
2019-03-31 23:09:49 +08:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
if (ch > 0x1F && ch < 0x7F)
|
2019-03-31 01:37:06 +08:00
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
string_buffer.push_back(char_traits::to_char_type(ch));
|
2019-03-31 01:37:06 +08:00
|
|
|
break;
|
|
|
|
|
}
|
2019-03-31 23:09:49 +08:00
|
|
|
else
|
|
|
|
|
{
|
2019-03-31 01:37:06 +08:00
|
|
|
return token_type::parse_error;
|
2019-03-31 23:09:49 +08:00
|
|
|
}
|
|
|
|
|
}
|
2019-03-31 01:37:06 +08:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
token_type scan_number()
|
|
|
|
|
{
|
|
|
|
|
is_negative = false;
|
|
|
|
|
number_value = static_cast<float_type>(0.0);
|
|
|
|
|
|
|
|
|
|
if (current == '-')
|
|
|
|
|
{
|
|
|
|
|
return scan_negative();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (current == '0')
|
|
|
|
|
{
|
|
|
|
|
return scan_zero();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return scan_integer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
token_type scan_negative()
|
|
|
|
|
{
|
|
|
|
|
if (current == '-')
|
|
|
|
|
{
|
|
|
|
|
is_negative = true;
|
|
|
|
|
read_next();
|
|
|
|
|
|
|
|
|
|
return scan_integer();
|
|
|
|
|
}
|
|
|
|
|
return token_type::parse_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
token_type scan_zero()
|
|
|
|
|
{
|
|
|
|
|
if (current == '0')
|
|
|
|
|
{
|
|
|
|
|
if (read_next() == '.')
|
|
|
|
|
return scan_float();
|
|
|
|
|
else
|
|
|
|
|
return token_type::value_integer;
|
|
|
|
|
}
|
|
|
|
|
return token_type::parse_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
token_type scan_integer()
|
|
|
|
|
{
|
|
|
|
|
if (std::isdigit(current))
|
|
|
|
|
{
|
|
|
|
|
number_value = static_cast<float_type>(current - '0');
|
|
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
const auto ch = read_next();
|
|
|
|
|
if (ch == '.')
|
|
|
|
|
return scan_float();
|
|
|
|
|
|
|
|
|
|
if (ch == 'e' || ch == 'E')
|
|
|
|
|
return scan_exponent();
|
|
|
|
|
|
|
|
|
|
if (std::isdigit(ch))
|
|
|
|
|
number_value = number_value * 10 + (ch - '0');
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return token_type::value_integer;
|
|
|
|
|
}
|
|
|
|
|
return token_type::parse_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
token_type scan_float()
|
|
|
|
|
{
|
|
|
|
|
if (current != '.')
|
|
|
|
|
return token_type::parse_error;
|
|
|
|
|
|
|
|
|
|
if (std::isdigit(read_next()))
|
|
|
|
|
{
|
|
|
|
|
float_type fraction = static_cast<float_type>(0.1);
|
|
|
|
|
number_value += static_cast<float_type>(current - '0') * fraction;
|
|
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
const auto ch = read_next();
|
|
|
|
|
if (ch == 'e' || ch == 'E')
|
|
|
|
|
return scan_exponent();
|
|
|
|
|
|
|
|
|
|
if (std::isdigit(ch))
|
|
|
|
|
{
|
|
|
|
|
fraction *= static_cast<float_type>(0.1);
|
|
|
|
|
number_value += static_cast<float_type>(ch - '0') * fraction;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return token_type::value_float;
|
|
|
|
|
}
|
|
|
|
|
return token_type::parse_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
token_type scan_exponent()
|
|
|
|
|
{
|
|
|
|
|
if (current != 'e' && current != 'E')
|
|
|
|
|
return token_type::parse_error;
|
|
|
|
|
|
|
|
|
|
// skip current char
|
|
|
|
|
read_next();
|
|
|
|
|
|
|
|
|
|
if ((std::isdigit(current) && current != '0') || (current == '-') || (current == '+'))
|
|
|
|
|
{
|
|
|
|
|
float_type base = 10;
|
|
|
|
|
if (current == '+')
|
|
|
|
|
{
|
|
|
|
|
read_next();
|
|
|
|
|
}
|
|
|
|
|
else if (current == '-')
|
|
|
|
|
{
|
|
|
|
|
base = static_cast<float_type>(0.1);
|
|
|
|
|
read_next();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int exponent = static_cast<unsigned int>(current - '0');
|
|
|
|
|
while (std::isdigit(read_next()))
|
|
|
|
|
{
|
|
|
|
|
exponent = (exponent * 10) + static_cast<unsigned int>(current - '0');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float_type power = 1;
|
|
|
|
|
for (; exponent; exponent >>= 1, base *= base)
|
|
|
|
|
if (exponent & 1)
|
|
|
|
|
power *= base;
|
|
|
|
|
|
|
|
|
|
number_value *= power;
|
|
|
|
|
return token_type::value_float;
|
|
|
|
|
}
|
|
|
|
|
return token_type::parse_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
integer_type token_to_integer() const
|
|
|
|
|
{
|
|
|
|
|
integer_type integer = static_cast<integer_type>(number_value);
|
|
|
|
|
return is_negative ? -integer : integer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float_type token_to_float() const
|
|
|
|
|
{
|
|
|
|
|
return is_negative ? -number_value : number_value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string_type token_to_string() const
|
|
|
|
|
{
|
|
|
|
|
return string_buffer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
input_adapter<char_type>* adapter;
|
|
|
|
|
typename char_traits::int_type current;
|
|
|
|
|
|
|
|
|
|
bool is_negative;
|
|
|
|
|
float_type number_value;
|
|
|
|
|
string_type string_buffer;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <typename _BasicJsonTy>
|
|
|
|
|
struct json_parser
|
|
|
|
|
{
|
|
|
|
|
using string_type = typename _BasicJsonTy::string_type;
|
|
|
|
|
using char_type = typename _BasicJsonTy::char_type;
|
|
|
|
|
using integer_type = typename _BasicJsonTy::integer_type;
|
|
|
|
|
using float_type = typename _BasicJsonTy::float_type;
|
|
|
|
|
using boolean_type = typename _BasicJsonTy::boolean_type;
|
|
|
|
|
using array_type = typename _BasicJsonTy::array_type;
|
|
|
|
|
using object_type = typename _BasicJsonTy::object_type;
|
|
|
|
|
using char_traits = std::char_traits<char_type>;
|
|
|
|
|
|
|
|
|
|
json_parser(input_adapter<char_type>* adapter)
|
|
|
|
|
: lexer(adapter)
|
|
|
|
|
, last_token(token_type::uninitialized)
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
void parse(_BasicJsonTy& json)
|
|
|
|
|
{
|
|
|
|
|
parse_value(json);
|
|
|
|
|
|
|
|
|
|
if (get_token() != token_type::end_of_input)
|
2019-03-31 23:09:49 +08:00
|
|
|
throw json_parse_error("unexpected token, expect end");
|
2019-03-31 01:37:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
token_type get_token()
|
|
|
|
|
{
|
|
|
|
|
last_token = lexer.scan();
|
|
|
|
|
return last_token;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void parse_value(_BasicJsonTy& json)
|
|
|
|
|
{
|
|
|
|
|
switch (get_token())
|
|
|
|
|
{
|
|
|
|
|
case token_type::literal_true:
|
|
|
|
|
json = JsonType::Boolean;
|
|
|
|
|
json.value_.data.boolean = true;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case token_type::literal_false:
|
|
|
|
|
json = JsonType::Boolean;
|
|
|
|
|
json.value_.data.boolean = false;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case token_type::literal_null:
|
|
|
|
|
json = JsonType::Null;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case token_type::value_string:
|
|
|
|
|
json = lexer.token_to_string();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case token_type::value_integer:
|
|
|
|
|
json = lexer.token_to_integer();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case token_type::value_float:
|
|
|
|
|
json = lexer.token_to_float();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case token_type::begin_array:
|
|
|
|
|
json = JsonType::Array;
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
json.value_.data.vector->push_back(_BasicJsonTy());
|
|
|
|
|
parse_value(json.value_.data.vector->back());
|
|
|
|
|
|
|
|
|
|
// read ','
|
|
|
|
|
if (get_token() != token_type::value_separator)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (last_token != token_type::end_array)
|
2019-03-31 23:09:49 +08:00
|
|
|
throw json_parse_error("unexpected token in array");
|
2019-03-31 01:37:06 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case token_type::begin_object:
|
|
|
|
|
json = JsonType::Object;
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
if (get_token() != token_type::value_string)
|
|
|
|
|
break;
|
|
|
|
|
string_type key = lexer.token_to_string();
|
|
|
|
|
|
|
|
|
|
if (get_token() != token_type::name_separator)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
_BasicJsonTy object;
|
|
|
|
|
parse_value(object);
|
|
|
|
|
json.value_.data.object->insert(std::make_pair(key, object));
|
|
|
|
|
|
|
|
|
|
// read ','
|
|
|
|
|
if (get_token() != token_type::value_separator)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (last_token != token_type::end_object)
|
2019-03-31 23:09:49 +08:00
|
|
|
throw json_parse_error("unexpected token in object");
|
2019-03-31 01:37:06 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
// unexpected token
|
2019-03-31 23:09:49 +08:00
|
|
|
throw json_parse_error("unexpected token");
|
2019-03-31 01:37:06 +08:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
json_lexer<_BasicJsonTy> lexer;
|
|
|
|
|
token_type last_token;
|
|
|
|
|
};
|
|
|
|
|
} // end of namespace __json_detail
|
|
|
|
|
|
|
|
|
|
namespace __json_detail
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
// json_value_getter
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
template <typename _BasicJsonTy>
|
|
|
|
|
struct json_value_getter
|
|
|
|
|
{
|
|
|
|
|
using string_type = typename _BasicJsonTy::string_type;
|
|
|
|
|
using char_type = typename _BasicJsonTy::char_type;
|
|
|
|
|
using integer_type = typename _BasicJsonTy::integer_type;
|
|
|
|
|
using float_type = typename _BasicJsonTy::float_type;
|
|
|
|
|
using boolean_type = typename _BasicJsonTy::boolean_type;
|
|
|
|
|
using array_type = typename _BasicJsonTy::array_type;
|
|
|
|
|
using object_type = typename _BasicJsonTy::object_type;
|
|
|
|
|
|
|
|
|
|
static inline void assign(const _BasicJsonTy& json, object_type& value)
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
if (!json.is_object()) throw json_type_error("json value type must be object");
|
2019-03-31 01:37:06 +08:00
|
|
|
value = *json.value_.data.object;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void assign(const _BasicJsonTy& json, array_type& value)
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
if (!json.is_array()) throw json_type_error("json value type must be array");
|
2019-03-31 01:37:06 +08:00
|
|
|
value = *json.value_.data.vector;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void assign(const _BasicJsonTy& json, string_type& value)
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
if (!json.is_string()) throw json_type_error("json value type must be string");
|
2019-03-31 01:37:06 +08:00
|
|
|
value = *json.value_.data.string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void assign(const _BasicJsonTy& json, boolean_type& value)
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
if (!json.is_boolean()) throw json_type_error("json value type must be boolean");
|
2019-03-31 01:37:06 +08:00
|
|
|
value = json.value_.data.boolean;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void assign(const _BasicJsonTy& json, integer_type& value)
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
if (!json.is_integer()) throw json_type_error("json value type must be integer");
|
2019-03-31 01:37:06 +08:00
|
|
|
value = json.value_.data.number_integer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <
|
|
|
|
|
typename _IntegerTy,
|
|
|
|
|
typename std::enable_if<std::is_integral<_IntegerTy>::value, int>::type = 0>
|
|
|
|
|
static inline void assign(const _BasicJsonTy& json, _IntegerTy& value)
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
if (!json.is_integer()) throw json_type_error("json value type must be integer");
|
2019-03-31 01:37:06 +08:00
|
|
|
value = static_cast<_IntegerTy>(json.value_.data.number_integer);
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-31 23:09:49 +08:00
|
|
|
static inline void assign(const _BasicJsonTy& json, float_type& value)
|
|
|
|
|
{
|
|
|
|
|
if (!json.is_float()) throw json_type_error("json value type must be float");
|
|
|
|
|
value = json.value_.data.number_float;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-31 01:37:06 +08:00
|
|
|
template <
|
|
|
|
|
typename _FloatingTy,
|
|
|
|
|
typename std::enable_if<std::is_floating_point<_FloatingTy>::value, int>::type = 0>
|
|
|
|
|
static inline void assign(const _BasicJsonTy& json, _FloatingTy& value)
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
if (!json.is_float()) throw json_type_error("json value type must be float");
|
2019-03-31 01:37:06 +08:00
|
|
|
value = static_cast<_FloatingTy>(json.value_.data.number_float);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
} // end of namespace __json_detail
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// basic_json
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
E2D_DECLARE_BASIC_JSON_TEMPLATE
|
|
|
|
|
class basic_json
|
|
|
|
|
{
|
|
|
|
|
friend struct __json_detail::iterator_impl<basic_json>;
|
|
|
|
|
friend struct __json_detail::iterator_impl<const basic_json>;
|
|
|
|
|
friend struct __json_detail::json_serializer<basic_json>;
|
|
|
|
|
friend struct __json_detail::json_parser<basic_json>;
|
|
|
|
|
friend struct __json_detail::json_value_getter<basic_json>;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
template <typename _Ty>
|
|
|
|
|
using allocator_type = _Allocator<_Ty>;
|
|
|
|
|
using size_type = std::size_t;
|
|
|
|
|
using difference_type = std::ptrdiff_t;
|
|
|
|
|
using string_type = _StringTy;
|
|
|
|
|
using char_type = typename _StringTy::value_type;
|
|
|
|
|
using integer_type = _IntegerTy;
|
|
|
|
|
using float_type = _FloatTy;
|
|
|
|
|
using boolean_type = _BooleanTy;
|
|
|
|
|
using array_type = typename _ArrayTy<basic_json, allocator_type<basic_json>>;
|
|
|
|
|
using object_type = typename _ObjectTy<string_type, basic_json, std::less<string_type>, allocator_type<std::pair<const string_type, basic_json>>>;
|
|
|
|
|
using initializer_list = std::initializer_list<basic_json>;
|
|
|
|
|
|
|
|
|
|
using iterator = __json_detail::iterator_impl<basic_json>;
|
|
|
|
|
using const_iterator = __json_detail::iterator_impl<const basic_json>;
|
|
|
|
|
using reverse_iterator = std::reverse_iterator<iterator>;
|
|
|
|
|
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
basic_json() {}
|
|
|
|
|
|
|
|
|
|
basic_json(std::nullptr_t) {}
|
|
|
|
|
|
|
|
|
|
basic_json(const JsonType type) : value_(type) {}
|
|
|
|
|
|
|
|
|
|
basic_json(basic_json const& other) : value_(other.value_) {}
|
|
|
|
|
|
|
|
|
|
basic_json(basic_json&& other) : value_(std::move(other.value_))
|
|
|
|
|
{
|
|
|
|
|
// invalidate payload
|
|
|
|
|
other.value_.type = JsonType::Null;
|
|
|
|
|
other.value_.data.object = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
basic_json(string_type const& value) : value_(value) {}
|
|
|
|
|
|
|
|
|
|
template <
|
|
|
|
|
typename _CompatibleTy,
|
|
|
|
|
typename std::enable_if<std::is_constructible<string_type, _CompatibleTy>::value, int>::type = 0>
|
|
|
|
|
basic_json(const _CompatibleTy& value)
|
|
|
|
|
{
|
|
|
|
|
value_.type = JsonType::String;
|
|
|
|
|
value_.data.string = value_.template create<string_type>(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
basic_json(array_type const& arr)
|
|
|
|
|
: value_(arr)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
basic_json(object_type const& object)
|
|
|
|
|
: value_(object)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
basic_json(integer_type value)
|
|
|
|
|
: value_(value)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <
|
|
|
|
|
typename _IntegerTy,
|
|
|
|
|
typename std::enable_if<std::is_integral<_IntegerTy>::value, int>::type = 0>
|
|
|
|
|
basic_json(_IntegerTy value)
|
|
|
|
|
: value_(static_cast<integer_type>(value))
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
basic_json(float_type value)
|
|
|
|
|
: value_(value)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <
|
|
|
|
|
typename _FloatingTy,
|
|
|
|
|
typename std::enable_if<std::is_floating_point<_FloatingTy>::value, int>::type = 0>
|
|
|
|
|
basic_json(_FloatingTy value)
|
|
|
|
|
: value_(static_cast<float_type>(value))
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
basic_json(boolean_type value)
|
|
|
|
|
: value_(value)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-01 22:02:46 +08:00
|
|
|
basic_json(initializer_list const& init_list)
|
2019-03-31 01:37:06 +08:00
|
|
|
{
|
|
|
|
|
bool is_an_object = std::all_of(init_list.begin(), init_list.end(), [](const basic_json& json)
|
|
|
|
|
{
|
|
|
|
|
return (json.is_array() && json.size() == 2 && json[0].is_string());
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (is_an_object)
|
|
|
|
|
{
|
|
|
|
|
value_ = JsonType::Object;
|
|
|
|
|
|
|
|
|
|
std::for_each(init_list.begin(), init_list.end(), [this](const basic_json& json)
|
|
|
|
|
{
|
|
|
|
|
value_.data.object->emplace(
|
|
|
|
|
*((*json.value_.data.vector)[0].value_.data.string),
|
|
|
|
|
(*json.value_.data.vector)[1]
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
value_ = JsonType::Array;
|
|
|
|
|
value_.data.vector->reserve(init_list.size());
|
|
|
|
|
value_.data.vector->assign(init_list.begin(), init_list.end());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-01 22:02:46 +08:00
|
|
|
static inline basic_json object(initializer_list const& init_list)
|
|
|
|
|
{
|
|
|
|
|
if (init_list.size() != 2 || !(*init_list.begin()).is_string())
|
|
|
|
|
{
|
|
|
|
|
throw json_type_error("cannot create object from initializer_list");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
basic_json json;
|
|
|
|
|
json.value_ = JsonType::Object;
|
|
|
|
|
json.value_.data.object->emplace(*((*init_list.begin()).value_.data.string), *(init_list.begin() + 1));
|
|
|
|
|
return json;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline basic_json array(initializer_list const& init_list)
|
|
|
|
|
{
|
|
|
|
|
basic_json json;
|
|
|
|
|
json.value_ = JsonType::Array;
|
|
|
|
|
|
|
|
|
|
if (init_list.size())
|
|
|
|
|
{
|
|
|
|
|
json.value_.data.vector->reserve(init_list.size());
|
|
|
|
|
json.value_.data.vector->assign(init_list.begin(), init_list.end());
|
|
|
|
|
}
|
|
|
|
|
return json;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-31 01:37:06 +08:00
|
|
|
inline bool is_object() const { return value_.type == JsonType::Object; }
|
|
|
|
|
|
|
|
|
|
inline bool is_array() const { return value_.type == JsonType::Array; }
|
|
|
|
|
|
|
|
|
|
inline bool is_string() const { return value_.type == JsonType::String; }
|
|
|
|
|
|
|
|
|
|
inline bool is_boolean() const { return value_.type == JsonType::Boolean; }
|
|
|
|
|
|
|
|
|
|
inline bool is_integer() const { return value_.type == JsonType::Integer; }
|
|
|
|
|
|
|
|
|
|
inline bool is_float() const { return value_.type == JsonType::Float; }
|
|
|
|
|
|
|
|
|
|
inline bool is_number() const { return is_integer() || is_float(); }
|
|
|
|
|
|
|
|
|
|
inline bool is_null() const { return value_.type == JsonType::Null; }
|
|
|
|
|
|
|
|
|
|
inline JsonType type() const { return value_.type; }
|
|
|
|
|
|
|
|
|
|
inline string_type type_name() const
|
|
|
|
|
{
|
|
|
|
|
switch (type())
|
|
|
|
|
{
|
|
|
|
|
case JsonType::Object:
|
|
|
|
|
return string_type(L"object");
|
|
|
|
|
case JsonType::Array:
|
|
|
|
|
return string_type(L"array");
|
|
|
|
|
case JsonType::String:
|
|
|
|
|
return string_type(L"string");
|
|
|
|
|
case JsonType::Integer:
|
|
|
|
|
return string_type(L"integer");
|
|
|
|
|
case JsonType::Float:
|
|
|
|
|
return string_type(L"float");
|
|
|
|
|
case JsonType::Boolean:
|
|
|
|
|
return string_type(L"boolean");
|
|
|
|
|
case JsonType::Null:
|
|
|
|
|
return string_type(L"null");
|
|
|
|
|
}
|
|
|
|
|
return string_type();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void swap(basic_json& rhs) { value_.swap(rhs.value_); }
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
inline iterator begin() { iterator iter(this); iter.set_begin(); return iter; }
|
|
|
|
|
inline const_iterator begin() const { return cbegin(); }
|
|
|
|
|
inline const_iterator cbegin() const { const_iterator iter(this); iter.set_begin(); return iter; }
|
|
|
|
|
inline iterator end() { iterator iter(this); iter.set_end(); return iter; }
|
|
|
|
|
inline const_iterator end() const { return cend(); }
|
|
|
|
|
inline const_iterator cend() const { const_iterator iter(this); iter.set_end(); return iter; }
|
|
|
|
|
inline reverse_iterator rbegin() { return reverse_iterator(end()); }
|
|
|
|
|
inline const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
|
|
|
|
|
inline const_reverse_iterator crbegin() const { return rbegin(); }
|
|
|
|
|
inline reverse_iterator rend() { return reverse_iterator(begin()); }
|
|
|
|
|
inline const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
|
|
|
|
|
inline const_reverse_iterator crend() const { return rend(); }
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
inline size_type size() const
|
|
|
|
|
{
|
|
|
|
|
switch (type())
|
|
|
|
|
{
|
|
|
|
|
case JsonType::Null:
|
|
|
|
|
return 0;
|
|
|
|
|
case JsonType::Array:
|
|
|
|
|
return value_.data.vector->size();
|
|
|
|
|
case JsonType::Object:
|
|
|
|
|
return value_.data.object->size();
|
|
|
|
|
default:
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool empty() const
|
|
|
|
|
{
|
|
|
|
|
if (is_null())
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (is_object())
|
|
|
|
|
return value_.data.object->empty();
|
|
|
|
|
|
|
|
|
|
if (is_array())
|
|
|
|
|
return value_.data.vector->empty();
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename _Kty>
|
|
|
|
|
inline const_iterator find(_Kty && key) const
|
|
|
|
|
{
|
|
|
|
|
if (is_object())
|
|
|
|
|
{
|
|
|
|
|
const_iterator iter;
|
|
|
|
|
iter.it_.object_iter = value_.data.object->find(std::forward<_Kty>(key));
|
|
|
|
|
return iter;
|
|
|
|
|
}
|
|
|
|
|
return cend();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename _Kty>
|
|
|
|
|
inline size_type count(_Kty && key) const
|
|
|
|
|
{
|
|
|
|
|
return is_object() ? value_.data.object->count(std::forward<_Kty>(key)) : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline size_type erase(const typename object_type::key_type& key)
|
|
|
|
|
{
|
|
|
|
|
if (!is_object())
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
throw json_invalid_key("cannot use erase() with non-object value");
|
2019-03-31 01:37:06 +08:00
|
|
|
}
|
|
|
|
|
return value_.data.object->erase(key);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void erase(const size_type index)
|
|
|
|
|
{
|
|
|
|
|
if (!is_array())
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
throw json_invalid_key("cannot use erase() with non-array value");
|
2019-03-31 01:37:06 +08:00
|
|
|
}
|
|
|
|
|
value_.data.vector->erase(value_.data.vector->begin() + static_cast<difference_type>(index));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<
|
|
|
|
|
class _IteratorTy,
|
|
|
|
|
typename std::enable_if<
|
|
|
|
|
std::is_same<_IteratorTy, iterator>::value ||
|
|
|
|
|
std::is_same<_IteratorTy, const_iterator>::value, int
|
|
|
|
|
>::type = 0>
|
|
|
|
|
inline _IteratorTy erase(_IteratorTy pos)
|
|
|
|
|
{
|
|
|
|
|
_IteratorTy result = end();
|
|
|
|
|
|
|
|
|
|
switch (type())
|
|
|
|
|
{
|
|
|
|
|
case JsonType::Object:
|
|
|
|
|
{
|
|
|
|
|
result.it_.object_iter = value_.data.object->erase(pos.it_.object_iter);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case JsonType::Array:
|
|
|
|
|
{
|
|
|
|
|
result.it_.array_iter = value_.data.vector->erase(pos.it_.array_iter);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
2019-03-31 23:09:49 +08:00
|
|
|
throw json_invalid_iterator("cannot use erase() with non-object & non-array value");
|
2019-03-31 01:37:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<
|
|
|
|
|
class _IteratorTy,
|
|
|
|
|
typename std::enable_if<
|
|
|
|
|
std::is_same<_IteratorTy, iterator>::value ||
|
|
|
|
|
std::is_same<_IteratorTy, const_iterator>::value, int
|
|
|
|
|
>::type = 0>
|
|
|
|
|
inline _IteratorTy erase(_IteratorTy first, _IteratorTy last)
|
|
|
|
|
{
|
|
|
|
|
_IteratorTy result = end();
|
|
|
|
|
|
|
|
|
|
switch (type())
|
|
|
|
|
{
|
|
|
|
|
case JsonType::Object:
|
|
|
|
|
{
|
|
|
|
|
result.it_.object_iter = value_.data.object->erase(first.it_.object_iter, last.it_.object_iter);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case JsonType::Array:
|
|
|
|
|
{
|
|
|
|
|
result.it_.array_iter = value_.data.vector->erase(first.it_.array_iter, last.it_.array_iter);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
2019-03-31 23:09:49 +08:00
|
|
|
throw json_invalid_iterator("cannot use erase() with non-object & non-array value");
|
2019-03-31 01:37:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void push_back(basic_json&& json)
|
|
|
|
|
{
|
|
|
|
|
if (!is_null() && !is_array())
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
throw json_type_error("cannot use push_back() with non-array value");
|
2019-03-31 01:37:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_null())
|
|
|
|
|
{
|
|
|
|
|
value_ = JsonType::Array;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
value_.data.vector->push_back(std::move(json));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline basic_json& operator+=(basic_json&& json)
|
|
|
|
|
{
|
|
|
|
|
push_back(std::move(json));
|
|
|
|
|
return (*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void clear()
|
|
|
|
|
{
|
|
|
|
|
switch (type())
|
|
|
|
|
{
|
|
|
|
|
case JsonType::Integer:
|
|
|
|
|
{
|
|
|
|
|
value_.data.number_integer = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case JsonType::Float:
|
|
|
|
|
{
|
|
|
|
|
value_.data.number_float = static_cast<float_type>(0.0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case JsonType::Boolean:
|
|
|
|
|
{
|
|
|
|
|
value_.data.boolean = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case JsonType::String:
|
|
|
|
|
{
|
|
|
|
|
value_.data.string->clear();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case JsonType::Array:
|
|
|
|
|
{
|
|
|
|
|
value_.data.vector->clear();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case JsonType::Object:
|
|
|
|
|
{
|
|
|
|
|
value_.data.object->clear();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// GET value functions
|
|
|
|
|
|
|
|
|
|
inline bool get_value(boolean_type& val) const
|
|
|
|
|
{
|
|
|
|
|
if (is_boolean())
|
|
|
|
|
{
|
|
|
|
|
val = value_.data.boolean;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool get_value(integer_type& val) const
|
|
|
|
|
{
|
|
|
|
|
if (is_integer())
|
|
|
|
|
{
|
|
|
|
|
val = value_.data.number_integer;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool get_value(float_type& val) const
|
|
|
|
|
{
|
|
|
|
|
if (is_float())
|
|
|
|
|
{
|
|
|
|
|
val = value_.data.number_float;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <
|
|
|
|
|
typename _IntegerTy,
|
|
|
|
|
typename std::enable_if<std::is_integral<_IntegerTy>::value, int>::type = 0>
|
|
|
|
|
inline bool get_value(_IntegerTy& val) const
|
|
|
|
|
{
|
|
|
|
|
if (is_integer())
|
|
|
|
|
{
|
|
|
|
|
val = static_cast<_IntegerTy>(value_.data.number_integer);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <
|
|
|
|
|
typename _FloatingTy,
|
|
|
|
|
typename std::enable_if<std::is_floating_point<_FloatingTy>::value, int>::type = 0>
|
|
|
|
|
inline bool get_value(_FloatingTy& val) const
|
|
|
|
|
{
|
|
|
|
|
if (is_float())
|
|
|
|
|
{
|
|
|
|
|
val = static_cast<_FloatingTy>(value_.data.number_float);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool get_value(array_type& val) const
|
|
|
|
|
{
|
|
|
|
|
if (is_array())
|
|
|
|
|
{
|
|
|
|
|
val.assign((*value_.data.vector).begin(), (*value_.data.vector).end());
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool get_value(string_type& val) const
|
|
|
|
|
{
|
|
|
|
|
if (is_string())
|
|
|
|
|
{
|
|
|
|
|
val.assign(*value_.data.string);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool get_value(object_type& val) const
|
|
|
|
|
{
|
|
|
|
|
if (is_object())
|
|
|
|
|
{
|
|
|
|
|
val.assign(*value_.data.object);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boolean_type as_bool() const
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
if (!is_boolean()) throw json_type_error("json value must be boolean");
|
2019-03-31 01:37:06 +08:00
|
|
|
return value_.data.boolean;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
integer_type as_int() const
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
if (!is_integer()) throw json_type_error("json value must be integer");
|
2019-03-31 01:37:06 +08:00
|
|
|
return value_.data.number_integer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float_type as_float() const
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
if (!is_float()) throw json_type_error("json value must be float");
|
2019-03-31 01:37:06 +08:00
|
|
|
return value_.data.number_float;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const array_type& as_array() const
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
if (!is_array()) throw json_type_error("json value must be array");
|
2019-03-31 01:37:06 +08:00
|
|
|
return *value_.data.vector;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const string_type& as_string() const
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
if (!is_string()) throw json_type_error("json value must be string");
|
2019-03-31 01:37:06 +08:00
|
|
|
return *value_.data.string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const object_type& as_object() const
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
if (!is_object()) throw json_type_error("json value must be object");
|
2019-03-31 01:37:06 +08:00
|
|
|
return *value_.data.object;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename _Ty>
|
|
|
|
|
_Ty get() const
|
|
|
|
|
{
|
|
|
|
|
_Ty value;
|
|
|
|
|
__json_detail::json_value_getter<basic_json>::assign(*this, value);
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// operator= functions
|
|
|
|
|
|
|
|
|
|
inline basic_json& operator=(basic_json const& other)
|
|
|
|
|
{
|
|
|
|
|
value_ = other.value_;
|
|
|
|
|
return (*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline basic_json& operator=(basic_json&& other)
|
|
|
|
|
{
|
|
|
|
|
value_ = std::move(other.value_);
|
|
|
|
|
return (*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline basic_json& operator=(std::nullptr_t)
|
|
|
|
|
{
|
|
|
|
|
value_ = nullptr;
|
|
|
|
|
return (*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// operator[] functions
|
|
|
|
|
|
|
|
|
|
inline basic_json& operator[](size_type index)
|
|
|
|
|
{
|
|
|
|
|
if (is_null())
|
|
|
|
|
{
|
|
|
|
|
value_ = JsonType::Array;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!is_array())
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
throw json_invalid_key("operator[] called on a non-array object");
|
2019-03-31 01:37:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (index >= value_.data.vector->size())
|
|
|
|
|
{
|
|
|
|
|
value_.data.vector->insert(value_.data.vector->end(),
|
|
|
|
|
index - value_.data.vector->size() + 1,
|
|
|
|
|
basic_json()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return (*value_.data.vector)[index];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline basic_json& operator[](size_type index) const
|
|
|
|
|
{
|
|
|
|
|
if (!is_array())
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
throw json_invalid_key("operator[] called on a non-array type");
|
2019-03-31 01:37:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (index >= value_.data.vector->size())
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
throw std::out_of_range("operator[] index out of range");
|
2019-03-31 01:37:06 +08:00
|
|
|
}
|
|
|
|
|
return (*value_.data.vector)[index];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline basic_json& operator[](const typename object_type::key_type& key)
|
|
|
|
|
{
|
|
|
|
|
if (is_null())
|
|
|
|
|
{
|
|
|
|
|
value_ = JsonType::Object;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!is_object())
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
throw json_invalid_key("operator[] called on a non-object type");
|
2019-03-31 01:37:06 +08:00
|
|
|
}
|
|
|
|
|
return (*value_.data.object)[key];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline basic_json& operator[](const typename object_type::key_type& key) const
|
|
|
|
|
{
|
|
|
|
|
if (!is_object())
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
throw json_invalid_key("operator[] called on a non-object object");
|
2019-03-31 01:37:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto iter = value_.data.object->find(key);
|
|
|
|
|
if (iter == value_.data.object->end())
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
throw std::out_of_range("operator[] key out of range");
|
2019-03-31 01:37:06 +08:00
|
|
|
}
|
|
|
|
|
return iter->second;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename _CharT>
|
|
|
|
|
inline basic_json& operator[](_CharT* key)
|
|
|
|
|
{
|
|
|
|
|
if (is_null())
|
|
|
|
|
{
|
|
|
|
|
value_ = JsonType::Object;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!is_object())
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
throw json_invalid_key("operator[] called on a non-object object");
|
2019-03-31 01:37:06 +08:00
|
|
|
}
|
|
|
|
|
return (*value_.data.object)[key];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename _CharT>
|
|
|
|
|
inline basic_json& operator[](_CharT* key) const
|
|
|
|
|
{
|
|
|
|
|
if (!is_object())
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
throw json_invalid_key("operator[] called on a non-object object");
|
2019-03-31 01:37:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto iter = value_.data.object->find(key);
|
|
|
|
|
if (iter == value_.data.object->end())
|
|
|
|
|
{
|
2019-03-31 23:09:49 +08:00
|
|
|
throw std::out_of_range("operator[] key out of range");
|
2019-03-31 01:37:06 +08:00
|
|
|
}
|
|
|
|
|
return iter->second;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// implicitly convert functions
|
|
|
|
|
|
|
|
|
|
template <typename _Ty>
|
|
|
|
|
inline operator _Ty() const
|
|
|
|
|
{
|
|
|
|
|
return get<_Ty>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// dumps functions
|
|
|
|
|
|
|
|
|
|
friend std::basic_ostream<char_type>& operator<<(std::basic_ostream<char_type>& out, const basic_json& json)
|
|
|
|
|
{
|
|
|
|
|
using char_type = typename std::basic_ostream<char_type>::char_type;
|
|
|
|
|
|
|
|
|
|
const bool pretty_print = (out.width() > 0);
|
|
|
|
|
const auto indentation = (pretty_print ? out.width() : 0);
|
|
|
|
|
out.width(0);
|
|
|
|
|
|
|
|
|
|
__json_detail::stream_output_adapter<char_type> adapter(out);
|
|
|
|
|
__json_detail::json_serializer<basic_json>(&adapter, out.fill()).dump(json, pretty_print, static_cast<unsigned int>(indentation));
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string_type dump(
|
|
|
|
|
const int indent = -1,
|
|
|
|
|
const char_type indent_char = ' ') const
|
|
|
|
|
{
|
|
|
|
|
string_type result;
|
|
|
|
|
__json_detail::string_output_adapter<string_type> adapter(result);
|
|
|
|
|
dump(&adapter, indent, indent_char);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void dump(
|
|
|
|
|
__json_detail::output_adapter<char_type>* adapter,
|
|
|
|
|
const int indent = -1,
|
|
|
|
|
const char_type indent_char = ' ') const
|
|
|
|
|
{
|
|
|
|
|
if (indent >= 0)
|
|
|
|
|
{
|
|
|
|
|
__json_detail::json_serializer<basic_json>(adapter, indent_char).dump(*this, true, static_cast<unsigned int>(indent));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
__json_detail::json_serializer<basic_json>(adapter, indent_char).dump(*this, false, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// parse functions
|
|
|
|
|
|
|
|
|
|
friend std::basic_istream<char_type>&
|
|
|
|
|
operator>>(std::basic_istream<char_type>& in, basic_json& json)
|
|
|
|
|
{
|
|
|
|
|
__json_detail::stream_input_adapter<char_type> adapter(in);
|
|
|
|
|
__json_detail::json_parser<basic_json>(&adapter).parse(json);
|
|
|
|
|
return in;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline basic_json parse(const string_type& str)
|
|
|
|
|
{
|
|
|
|
|
__json_detail::string_input_adapter<string_type> adapter(str);
|
|
|
|
|
return parse(&adapter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline basic_json parse(const char_type* str)
|
|
|
|
|
{
|
|
|
|
|
__json_detail::buffer_input_adapter<char_type> adapter(str);
|
|
|
|
|
return parse(&adapter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline basic_json parse(std::FILE* file)
|
|
|
|
|
{
|
|
|
|
|
__json_detail::file_input_adapter<char_type> adapter(file);
|
|
|
|
|
return parse(&adapter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline basic_json parse(__json_detail::input_adapter<char_type>* adapter)
|
|
|
|
|
{
|
|
|
|
|
basic_json result;
|
|
|
|
|
__json_detail::json_parser<basic_json>(adapter).parse(result);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// compare functions
|
|
|
|
|
|
|
|
|
|
friend bool operator==(const basic_json& lhs, const basic_json& rhs)
|
|
|
|
|
{
|
|
|
|
|
const auto lhs_type = lhs.type();
|
|
|
|
|
const auto rhs_type = rhs.type();
|
|
|
|
|
|
|
|
|
|
if (lhs_type == rhs_type)
|
|
|
|
|
{
|
|
|
|
|
switch (lhs_type)
|
|
|
|
|
{
|
|
|
|
|
case JsonType::Array:
|
|
|
|
|
return (*lhs.value_.data.vector == *rhs.value_.data.vector);
|
|
|
|
|
|
|
|
|
|
case JsonType::Object:
|
|
|
|
|
return (*lhs.value_.data.object == *rhs.value_.data.object);
|
|
|
|
|
|
|
|
|
|
case JsonType::Null:
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
case JsonType::String:
|
|
|
|
|
return (*lhs.value_.data.string == *rhs.value_.data.string);
|
|
|
|
|
|
|
|
|
|
case JsonType::Boolean:
|
|
|
|
|
return (lhs.value_.data.boolean == rhs.value_.data.boolean);
|
|
|
|
|
|
|
|
|
|
case JsonType::Integer:
|
|
|
|
|
return (lhs.value_.data.number_integer == rhs.value_.data.number_integer);
|
|
|
|
|
|
|
|
|
|
case JsonType::Float:
|
|
|
|
|
return (lhs.value_.data.number_float == rhs.value_.data.number_float);
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (lhs_type == JsonType::Integer && rhs_type == JsonType::Float)
|
|
|
|
|
{
|
|
|
|
|
return (static_cast<float_type>(lhs.value_.data.number_integer) == rhs.value_.data.number_float);
|
|
|
|
|
}
|
|
|
|
|
else if (lhs_type == JsonType::Float && rhs_type == JsonType::Integer)
|
|
|
|
|
{
|
|
|
|
|
return (lhs.value_.data.number_float == static_cast<float_type>(rhs.value_.data.number_integer));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
friend bool operator!=(const basic_json& lhs, const basic_json& rhs)
|
|
|
|
|
{
|
|
|
|
|
return !(lhs == rhs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
friend bool operator<(const basic_json& lhs, const basic_json& rhs)
|
|
|
|
|
{
|
|
|
|
|
const auto lhs_type = lhs.type();
|
|
|
|
|
const auto rhs_type = rhs.type();
|
|
|
|
|
|
|
|
|
|
if (lhs_type == rhs_type)
|
|
|
|
|
{
|
|
|
|
|
switch (lhs_type)
|
|
|
|
|
{
|
|
|
|
|
case JsonType::Array:
|
|
|
|
|
return (*lhs.value_.data.vector) < (*rhs.value_.data.vector);
|
|
|
|
|
|
|
|
|
|
case JsonType::Object:
|
|
|
|
|
return (*lhs.value_.data.object) < (*rhs.value_.data.object);
|
|
|
|
|
|
|
|
|
|
case JsonType::Null:
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
case JsonType::String:
|
|
|
|
|
return (*lhs.value_.data.string) < (*rhs.value_.data.string);
|
|
|
|
|
|
|
|
|
|
case JsonType::Boolean:
|
|
|
|
|
return (lhs.value_.data.boolean < rhs.value_.data.boolean);
|
|
|
|
|
|
|
|
|
|
case JsonType::Integer:
|
|
|
|
|
return (lhs.value_.data.number_integer < rhs.value_.data.number_integer);
|
|
|
|
|
|
|
|
|
|
case JsonType::Float:
|
|
|
|
|
return (lhs.value_.data.number_float < rhs.value_.data.number_float);
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (lhs_type == JsonType::Integer && rhs_type == JsonType::Float)
|
|
|
|
|
{
|
|
|
|
|
return (static_cast<float_type>(lhs.value_.data.number_integer) < rhs.value_.data.number_float);
|
|
|
|
|
}
|
|
|
|
|
else if (lhs_type == JsonType::Float && rhs_type == JsonType::Integer)
|
|
|
|
|
{
|
|
|
|
|
return (lhs.value_.data.number_float < static_cast<float_type>(rhs.value_.data.number_integer));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
friend bool operator<=(const basic_json& lhs, const basic_json& rhs)
|
|
|
|
|
{
|
|
|
|
|
return !(rhs < lhs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
friend bool operator>(const basic_json& lhs, const basic_json& rhs)
|
|
|
|
|
{
|
|
|
|
|
return rhs < lhs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
friend bool operator>=(const basic_json& lhs, const basic_json& rhs)
|
|
|
|
|
{
|
|
|
|
|
return !(lhs < rhs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
__json_detail::json_value<basic_json> value_;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace std
|
|
|
|
|
{
|
|
|
|
|
template<>
|
|
|
|
|
struct hash<::easy2d::Json>
|
|
|
|
|
{
|
|
|
|
|
std::size_t operator()(const ::easy2d::Json& json) const
|
|
|
|
|
{
|
|
|
|
|
return hash<::easy2d::Json::string_type>{}(json.dump());
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template<>
|
|
|
|
|
inline void swap<::easy2d::Json>(::easy2d::Json& lhs, ::easy2d::Json& rhs)
|
|
|
|
|
{
|
|
|
|
|
lhs.swap(rhs);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-04-04 14:25:13 +08:00
|
|
|
|
|
|
|
|
#undef E2D_DECLARE_BASIC_JSON_TEMPLATE
|
|
|
|
|
#undef E2D_DECLARE_BASIC_JSON_TPL_ARGS
|