// Copyright (c) 2018-2020 Jsonxx - 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 #include #include #include #include #include #include "json_value.hpp" #include "json_parser.hpp" #include "json_serializer.hpp" namespace jsonxx { template< template typename _ObjectTy = std::map, template typename _ArrayTy = std::vector, typename _StringTy = std::string, typename _IntegerTy = std::int32_t, typename _FloatTy = double, typename _BooleanTy = bool, template typename _Allocator = std::allocator> class basic_json; // // details of basic_json // #define DECLARE_BASIC_JSON_TEMPLATE \ template < \ template typename _ObjectTy, \ template typename _ArrayTy, \ typename _StringTy, \ typename _IntegerTy, \ typename _FloatTy, \ typename _BooleanTy, \ template typename _Allocator> #define DECLARE_BASIC_JSON_TPL_ARGS \ _ObjectTy, _ArrayTy, _StringTy, _IntegerTy, _FloatTy, _BooleanTy, _Allocator // // is_basic_json // template struct is_basic_json : std::false_type { }; DECLARE_BASIC_JSON_TEMPLATE struct is_basic_json> : std::true_type { }; // // basic_json // DECLARE_BASIC_JSON_TEMPLATE class basic_json { friend struct iterator_impl; friend struct iterator_impl; friend struct json_serializer; friend struct json_parser; friend struct json_value_getter; public: template 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 = _ArrayTy>; using object_type = _ObjectTy, allocator_type>>; using initializer_list = std::initializer_list; using iterator = iterator_impl; using const_iterator = iterator_impl; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; public: basic_json() {} basic_json(std::nullptr_t) {} basic_json(const json_type 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 = json_type::null; other.value_.data.object = nullptr; } basic_json(string_type const &value) : value_(value) {} template< typename _CompatibleTy, typename std::enable_if::value, int>::type = 0> basic_json(const _CompatibleTy &value) { value_.type = json_type::string; value_.data.string = value_.template create(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 _IntegerUTy, typename std::enable_if::value, int>::type = 0> basic_json(_IntegerUTy value) : value_(static_cast(value)) { } basic_json(float_type value) : value_(value) { } template< typename _FloatingTy, typename std::enable_if::value, int>::type = 0> basic_json(_FloatingTy value) : value_(static_cast(value)) { } basic_json(boolean_type value) : value_(value) { } basic_json(initializer_list const &init_list) { 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_ = json_type::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_ = json_type::array; value_.data.vector->reserve(init_list.size()); value_.data.vector->assign(init_list.begin(), init_list.end()); } } 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_ = json_type::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_ = json_type::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; } inline bool is_object() const { return value_.type == json_type::object; } inline bool is_array() const { return value_.type == json_type::array; } inline bool is_string() const { return value_.type == json_type::string; } inline bool is_boolean() const { return value_.type == json_type::boolean; } inline bool is_integer() const { return value_.type == json_type::number_integer; } inline bool is_float() const { return value_.type == json_type::number_float; } inline bool is_number() const { return is_integer() || is_float(); } inline bool is_null() const { return value_.type == json_type::null; } inline json_type type() const { return value_.type; } inline string_type type_name() const { switch (type()) { case json_type::object: return string_type("object"); case json_type::array: return string_type("array"); case json_type::string: return string_type("string"); case json_type::number_integer: return string_type("integer"); case json_type::number_float: return string_type("float"); case json_type::boolean: return string_type("boolean"); case json_type::null: return string_type("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 json_type::null: return 0; case json_type::array: return value_.data.vector->size(); case json_type::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 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 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()) { throw json_invalid_key("cannot use erase() with non-object value"); } return value_.data.object->erase(key); } inline void erase(const size_type index) { if (!is_array()) { throw json_invalid_key("cannot use erase() with non-array value"); } value_.data.vector->erase(value_.data.vector->begin() + static_cast(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 json_type::object: { result.it_.object_iter = value_.data.object->erase(pos.it_.object_iter); break; } case json_type::array: { result.it_.array_iter = value_.data.vector->erase(pos.it_.array_iter); break; } default: throw json_invalid_iterator("cannot use erase() with non-object & non-array value"); } 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 json_type::object: { result.it_.object_iter = value_.data.object->erase(first.it_.object_iter, last.it_.object_iter); break; } case json_type::array: { result.it_.array_iter = value_.data.vector->erase(first.it_.array_iter, last.it_.array_iter); break; } default: throw json_invalid_iterator("cannot use erase() with non-object & non-array value"); } return result; } inline void push_back(basic_json &&json) { if (!is_null() && !is_array()) { throw json_type_error("cannot use push_back() with non-array value"); } if (is_null()) { value_ = json_type::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 json_type::number_integer: { value_.data.number_integer = 0; break; } case json_type::number_float: { value_.data.number_float = static_cast(0.0); break; } case json_type::boolean: { value_.data.boolean = false; break; } case json_type::string: { value_.data.string->clear(); break; } case json_type::array: { value_.data.vector->clear(); break; } case json_type::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 _IntegerUTy, typename std::enable_if::value, int>::type = 0> inline bool get_value(_IntegerUTy &val) const { if (is_integer()) { val = static_cast<_IntegerUTy>(value_.data.number_integer); return true; } return false; } template< typename _FloatingTy, typename std::enable_if::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 { if (!is_boolean()) throw json_type_error("json value must be boolean"); return value_.data.boolean; } integer_type as_int() const { if (!is_integer()) throw json_type_error("json value must be integer"); return value_.data.number_integer; } float_type as_float() const { if (is_float()) throw json_type_error("json value must be float"); return value_.data.number_float; } float_type as_float_anynum() const { if (!is_number()) throw json_type_error("json value must be number"); if (is_integer()) return value_.data.number_integer; if (is_float()) return value_.data.number_float; throw json_type_error("json value can not be known"); } const array_type &as_array() const { if (!is_array()) throw json_type_error("json value must be array"); return *value_.data.vector; } const string_type &as_string() const { if (!is_string()) throw json_type_error("json value must be string"); return *value_.data.string; } const object_type &as_object() const { if (!is_object()) throw json_type_error("json value must be object"); return *value_.data.object; } template _Ty get() const { _Ty value; json_value_getter::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_ = json_type::array; } if (!is_array()) { throw json_invalid_key("operator[] called on a non-array object"); } 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()) { throw json_invalid_key("operator[] called on a non-array type"); } if (index >= value_.data.vector->size()) { throw std::out_of_range("operator[] index out of range"); } return (*value_.data.vector)[index]; } inline basic_json &operator[](const typename object_type::key_type &key) { if (is_null()) { value_ = json_type::object; } if (!is_object()) { throw json_invalid_key("operator[] called on a non-object type"); } return (*value_.data.object)[key]; } inline basic_json &operator[](const typename object_type::key_type &key) const { if (!is_object()) { throw json_invalid_key("operator[] called on a non-object object"); } auto iter = value_.data.object->find(key); if (iter == value_.data.object->end()) { throw std::out_of_range("operator[] key out of range"); } return iter->second; } template inline basic_json &operator[](_CharT *key) { if (is_null()) { value_ = json_type::object; } if (!is_object()) { throw json_invalid_key("operator[] called on a non-object object"); } return (*value_.data.object)[key]; } template inline basic_json &operator[](_CharT *key) const { if (!is_object()) { throw json_invalid_key("operator[] called on a non-object object"); } auto iter = value_.data.object->find(key); if (iter == value_.data.object->end()) { throw std::out_of_range("operator[] key out of range"); } return iter->second; } public: // implicitly convert functions template inline operator _Ty() const { return get<_Ty>(); } public: // dumps functions friend std::basic_ostream &operator<<(std::basic_ostream &out, const basic_json &json) { using char_type = typename std::basic_ostream::char_type; const bool pretty_print = (out.width() > 0); const auto indentation = (pretty_print ? out.width() : 0); out.width(0); stream_output_adapter adapter(out); json_serializer(&adapter, out.fill()).dump(json, pretty_print, static_cast(indentation)); return out; } string_type dump( const int indent = -1, const char_type indent_char = ' ') const { string_type result; string_output_adapter adapter(result); dump(&adapter, indent, indent_char); return result; } void dump( output_adapter *adapter, const int indent = -1, const char_type indent_char = ' ') const { if (indent >= 0) { json_serializer(adapter, indent_char).dump(*this, true, static_cast(indent)); } else { json_serializer(adapter, indent_char).dump(*this, false, 0); } } public: // parse functions friend std::basic_istream & operator>>(std::basic_istream &in, basic_json &json) { stream_input_adapter adapter(in); json_parser(&adapter).parse(json); return in; } static inline basic_json parse(const string_type &str) { string_input_adapter adapter(str); return parse(&adapter); } static inline basic_json parse(const char_type *str) { buffer_input_adapter adapter(str); return parse(&adapter); } static inline basic_json parse(std::FILE *file) { file_input_adapter adapter(file); return parse(&adapter); } static inline basic_json parse(input_adapter *adapter) { basic_json result; json_parser(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 json_type::array: return (*lhs.value_.data.vector == *rhs.value_.data.vector); case json_type::object: return (*lhs.value_.data.object == *rhs.value_.data.object); case json_type::null: return true; case json_type::string: return (*lhs.value_.data.string == *rhs.value_.data.string); case json_type::boolean: return (lhs.value_.data.boolean == rhs.value_.data.boolean); case json_type::number_integer: return (lhs.value_.data.number_integer == rhs.value_.data.number_integer); case json_type::number_float: return (lhs.value_.data.number_float == rhs.value_.data.number_float); default: return false; } } else if (lhs_type == json_type::number_integer && rhs_type == json_type::number_float) { return (static_cast(lhs.value_.data.number_integer) == rhs.value_.data.number_float); } else if (lhs_type == json_type::number_float && rhs_type == json_type::number_integer) { return (lhs.value_.data.number_float == static_cast(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 json_type::array: return (*lhs.value_.data.vector) < (*rhs.value_.data.vector); case json_type::object: return (*lhs.value_.data.object) < (*rhs.value_.data.object); case json_type::null: return false; case json_type::string: return (*lhs.value_.data.string) < (*rhs.value_.data.string); case json_type::boolean: return (lhs.value_.data.boolean < rhs.value_.data.boolean); case json_type::number_integer: return (lhs.value_.data.number_integer < rhs.value_.data.number_integer); case json_type::number_float: return (lhs.value_.data.number_float < rhs.value_.data.number_float); default: return false; } } else if (lhs_type == json_type::number_integer && rhs_type == json_type::number_float) { return (static_cast(lhs.value_.data.number_integer) < rhs.value_.data.number_float); } else if (lhs_type == json_type::number_float && rhs_type == json_type::number_integer) { return (lhs.value_.data.number_float < static_cast(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_value value_; }; } // namespace Jsonxx #undef DECLARE_BASIC_JSON_TEMPLATE #undef DECLARE_BASIC_JSON_TPL_ARGS