Skip to content

Instantly share code, notes, and snippets.

@Raclamusi
Last active December 29, 2023 07:21

Revisions

  1. Raclamusi revised this gist Dec 29, 2023. 1 changed file with 16 additions and 15 deletions.
    31 changes: 16 additions & 15 deletions SivJSON.cpp
    Original file line number Diff line number Diff line change
    @@ -62,7 +62,7 @@ namespace s3d

    using ValueType = nlohmann::json;

    using RefType = std::reference_wrapper<ValueType>;
    using RefType = std::shared_ptr<ValueType>;

    static constexpr size_t ValueIndex = 0;

    @@ -81,19 +81,20 @@ namespace s3d
    JSONDetail(Value, nlohmann::json&& _json)
    : m_json{ std::move(_json) } {}

    JSONDetail(Ref, nlohmann::json& _json)
    : m_json{ std::ref(_json) } {}
    template <class Parent>
    JSONDetail(Ref, const std::shared_ptr<Parent>& r, nlohmann::json& _json)
    : m_json{ RefType{ r, std::addressof(_json) } } {}

    nlohmann::json& get()
    {
    return (m_json.index() == ValueIndex) ?
    std::get<ValueType>(m_json) : std::get<RefType>(m_json).get();
    std::get<ValueType>(m_json) : *std::get<RefType>(m_json);
    }

    const nlohmann::json& get() const
    {
    return (m_json.index() == ValueIndex) ?
    std::get<ValueType>(m_json) : std::get<RefType>(m_json).get();
    std::get<ValueType>(m_json) : *std::get<RefType>(m_json);
    }
    };

    @@ -241,7 +242,7 @@ namespace s3d

    JSON JSONIterator::value() const
    {
    return JSON(std::make_shared<detail::JSONDetail>(detail::JSONDetail::Ref(), *m_detail->it));
    return JSON(std::make_shared<detail::JSONDetail>(detail::JSONDetail::Ref(), m_detail, *m_detail->it));
    }

    bool JSONIterator::operator ==(const JSONIterator& other) const noexcept
    @@ -372,7 +373,7 @@ namespace s3d

    const JSON JSONConstIterator::value() const
    {
    return JSON(std::make_shared<detail::JSONDetail>(detail::JSONDetail::Ref(), const_cast<nlohmann::json&>(*m_detail->it)));
    return JSON(std::make_shared<detail::JSONDetail>(detail::JSONDetail::Ref(), m_detail, const_cast<nlohmann::json&>(*m_detail->it)));
    }

    bool JSONConstIterator::operator ==(const JSONConstIterator& other) const noexcept
    @@ -438,7 +439,7 @@ namespace s3d

    JSON JSONIterationProxy::operator *() const
    {
    return JSON(std::make_shared<detail::JSONDetail>(detail::JSONDetail::Ref(), m_detail->it.value()));
    return JSON(std::make_shared<detail::JSONDetail>(detail::JSONDetail::Ref(), m_detail, m_detail->it.value()));
    }

    bool JSONIterationProxy::operator ==(const JSONIterationProxy& other) const noexcept
    @@ -546,7 +547,7 @@ namespace s3d

    JSON& JSON::operator =(JSON&& other) noexcept
    {
    m_detail = std::exchange(other.m_detail, std::make_shared<detail::JSONDetail>());
    *m_detail = std::exchange(*other.m_detail, detail::JSONDetail{});
    m_isValid = std::exchange(other.m_isValid, false);
    return *this;
    }
    @@ -816,7 +817,7 @@ namespace s3d
    }

    return JSON(std::make_shared<detail::JSONDetail>(
    detail::JSONDetail::Ref(), m_detail->get()[Unicode::ToUTF8(name)]));
    detail::JSONDetail::Ref(), m_detail, m_detail->get()[Unicode::ToUTF8(name)]));
    }

    const JSON JSON::operator [](const StringView name) const
    @@ -828,7 +829,7 @@ namespace s3d

    if (contains(name))
    {
    return JSON(std::make_shared<detail::JSONDetail>(detail::JSONDetail::Ref(), m_detail->get()[Unicode::ToUTF8(name)]));
    return JSON(std::make_shared<detail::JSONDetail>(detail::JSONDetail::Ref(), m_detail, m_detail->get()[Unicode::ToUTF8(name)]));
    }

    throw Error{ U"JSON::operator [](StringView) const: Key `{}` not found"_fmt(name) };
    @@ -846,7 +847,7 @@ namespace s3d
    throw Error{ U"JSON::operator [](size_t): The value is neither of type Array nor of type Null." };
    }

    return JSON(std::make_shared<detail::JSONDetail>(detail::JSONDetail::Ref(), m_detail->get()[index]));
    return JSON(std::make_shared<detail::JSONDetail>(detail::JSONDetail::Ref(), m_detail, m_detail->get()[index]));
    }

    const JSON JSON::operator [](const size_t index) const
    @@ -866,7 +867,7 @@ namespace s3d
    throw Error{ U"JSON::operator [](size_t) const: Index out of range (index: {} >= size: {})"_fmt(index, size()) };
    }

    return JSON(std::make_shared<detail::JSONDetail>(detail::JSONDetail::Ref(), m_detail->get()[index]));
    return JSON(std::make_shared<detail::JSONDetail>(detail::JSONDetail::Ref(), m_detail, m_detail->get()[index]));
    }

    JSON JSON::operator[](const JSONPointer& jsonPointer)
    @@ -888,7 +889,7 @@ namespace s3d

    if (isObject() || isArray() || isNull())
    {
    return JSON(std::make_shared<detail::JSONDetail>(detail::JSONDetail::Ref(), m_detail->get()[jsonPointer.m_detail->pointer]));
    return JSON(std::make_shared<detail::JSONDetail>(detail::JSONDetail::Ref(), m_detail, m_detail->get()[jsonPointer.m_detail->pointer]));
    }

    throw Error{ U"JSON::access(JSONPointer): Invalid JSON type. This function requires that the value be of type Object, Array or Null." };
    @@ -903,7 +904,7 @@ namespace s3d

    if (contains(jsonPointer))
    {
    return JSON(std::make_shared<detail::JSONDetail>(detail::JSONDetail::Ref(), m_detail->get()[jsonPointer.m_detail->pointer]));
    return JSON(std::make_shared<detail::JSONDetail>(detail::JSONDetail::Ref(), m_detail, m_detail->get()[jsonPointer.m_detail->pointer]));
    }

    throw Error{ U"JSON::access(JSONPointer) const: The element indicated by the JSON Pointer does not exist." };
  2. Raclamusi created this gist Dec 29, 2023.
    1,659 changes: 1,659 additions & 0 deletions SivJSON.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,1659 @@
    //-----------------------------------------------
    //
    // This file is part of the Siv3D Engine.
    //
    // Copyright (c) 2008-2023 Ryo Suzuki
    // Copyright (c) 2016-2023 OpenSiv3D Project
    //
    // Licensed under the MIT License.
    //
    //-----------------------------------------------

    # include <variant>
    # include <Siv3D/JSON.hpp>
    # include <Siv3D/JSONValidator.hpp>
    # include <Siv3D/TextReader.hpp>
    # include <Siv3D/TextWriter.hpp>
    # include <Siv3D/Unicode.hpp>
    # include <ThirdParty/nlohmann/json.hpp>
    # include <ThirdParty/nlohmann/json-schema.hpp>

    namespace s3d
    {
    namespace detail
    {
    struct JSONIteratorDetail
    {
    nlohmann::json::iterator it;

    JSONIteratorDetail() = default;

    explicit JSONIteratorDetail(nlohmann::json::iterator _it)
    : it{ _it } {}
    };

    struct JSONConstIteratorDetail
    {
    nlohmann::json::const_iterator it;

    JSONConstIteratorDetail() = default;

    explicit JSONConstIteratorDetail(nlohmann::json::const_iterator _it)
    : it{ _it } {}

    explicit JSONConstIteratorDetail(const JSONIteratorDetail& _it)
    : it{_it.it} {}

    explicit JSONConstIteratorDetail(JSONIteratorDetail&& _it)
    : it{std::move(_it).it} {}
    };

    struct JSONIterationProxyDetail
    {
    nlohmann::detail::iteration_proxy_value<nlohmann::json::iterator> it;

    explicit JSONIterationProxyDetail(nlohmann::detail::iteration_proxy_value<nlohmann::json::iterator> _it)
    : it{ _it } {}
    };

    class JSONDetail
    {
    private:

    using ValueType = nlohmann::json;

    using RefType = std::reference_wrapper<ValueType>;

    static constexpr size_t ValueIndex = 0;

    static constexpr size_t RefIndex = 1;

    std::variant<ValueType, RefType> m_json;

    public:

    struct Value {};

    struct Ref {};

    JSONDetail() = default;

    JSONDetail(Value, nlohmann::json&& _json)
    : m_json{ std::move(_json) } {}

    JSONDetail(Ref, nlohmann::json& _json)
    : m_json{ std::ref(_json) } {}

    nlohmann::json& get()
    {
    return (m_json.index() == ValueIndex) ?
    std::get<ValueType>(m_json) : std::get<RefType>(m_json).get();
    }

    const nlohmann::json& get() const
    {
    return (m_json.index() == ValueIndex) ?
    std::get<ValueType>(m_json) : std::get<RefType>(m_json).get();
    }
    };

    struct ValidationErrorHandler : nlohmann::json_schema::error_handler
    {
    void error(const nlohmann::json::json_pointer& pointer, const nlohmann::json& instance, const std::string& message) override
    {
    nlohmann::json tmp{ instance };

    details = JSONValidator::ValidationError {
    Unicode::FromUTF8(message),
    JSONPointer{std::make_shared<detail::JSONPointerDetail>(pointer)},
    JSON(std::make_shared<detail::JSONDetail>(JSONDetail::Value{}, std::move(tmp)))
    };
    }

    JSONValidator::ValidationError details;
    };

    struct JSONPointerDetail
    {
    nlohmann::json::json_pointer pointer;

    explicit JSONPointerDetail(const std::string& s = "")
    : pointer(s)
    {}
    };

    struct JSONValidatorDetail
    {
    nlohmann::json_schema::json_validator validator;

    JSONValidatorDetail() = default;

    JSONValidatorDetail(const JSONValidatorDetail&) = delete;

    JSONValidatorDetail(JSONValidatorDetail&&) = default;

    JSONValidatorDetail(nlohmann::json_schema::json_validator&& validator)
    : validator(std::move(validator))
    {}

    ~JSONValidatorDetail() = default;

    JSONValidatorDetail& operator=(const JSONValidatorDetail&) = delete;

    JSONValidatorDetail& operator=(JSONValidatorDetail&&) = default;
    };
    }

    //////////////////////////////////////////////////
    //
    // JSONIterator
    //
    //////////////////////////////////////////////////

    JSONIterator::JSONIterator(const JSONIterator& rhs)
    : m_parent{ rhs.m_parent }
    , m_index{ rhs.m_index }
    , m_detail{ std::make_shared<detail::JSONIteratorDetail>(*rhs.m_detail) } {}

    JSONIterator::JSONIterator(JSON* parent, JSONIterator::difference_type index, const detail::JSONIteratorDetail& d)
    : m_parent{ parent }
    , m_index{ index }
    , m_detail{ std::make_shared<detail::JSONIteratorDetail>(d.it) } {}

    JSONIterator& JSONIterator::operator =(const JSONIterator& rhs)
    {
    JSONIterator tmp{ rhs };

    this->m_parent = tmp.m_parent;
    this->m_index = tmp.m_index;
    this->m_detail = std::move(tmp.m_detail);

    return *this;
    }

    JSONIterator& JSONIterator::operator ++()
    {
    ++m_detail->it;

    ++m_index;

    return *this;
    }

    JSONIterator JSONIterator::operator ++(int)
    {
    const JSONIterator tmp{ *this };

    ++(*this);

    return tmp;
    }

    JSONIterator& JSONIterator::operator --()
    {
    --m_detail->it;

    --m_index;

    return *this;
    }

    JSONIterator JSONIterator::operator --(int)
    {
    const JSONIterator tmp{ *this };

    --(*this);

    return tmp;
    }

    JSONIterator JSONIterator::operator +(JSONIterator::difference_type index) const
    {
    const detail::JSONIteratorDetail tmp{ m_detail->it + index };

    return JSONIterator{ m_parent, (m_index + index), tmp };
    }

    JSONItem JSONIterator::operator *() const
    {
    return{ key(), value() };
    }

    String JSONIterator::key() const
    {
    if (m_parent != nullptr) SIV3D_LIKELY
    {
    switch (m_parent->getType())
    {
    case JSONValueType::Object:
    return Unicode::FromUTF8(m_detail->it.key());
    case JSONValueType::Array:
    return Format(m_index);
    default:
    return{};
    }
    }
    else SIV3D_UNLIKELY
    {
    throw Error{ U"This JSONIterator has not been constructed from any JSON." };
    }
    }

    JSON JSONIterator::value() const
    {
    return JSON(std::make_shared<detail::JSONDetail>(detail::JSONDetail::Ref(), *m_detail->it));
    }

    bool JSONIterator::operator ==(const JSONIterator& other) const noexcept
    {
    if ((not m_detail) || (not other.m_detail))
    {
    return (not m_detail) && (not other.m_detail);
    }

    return (m_detail->it == other.m_detail->it);
    }

    bool JSONIterator::operator !=(const JSONIterator& other) const noexcept
    {
    return (not (*this == other));
    }

    //////////////////////////////////////////////////
    //
    // JSONConstIterator
    //
    //////////////////////////////////////////////////

    JSONConstIterator::JSONConstIterator(const JSONIterator& rhs)
    : m_parent{ rhs.m_parent }
    , m_index{ rhs.m_index }
    , m_detail{ std::make_shared<detail::JSONConstIteratorDetail>(*rhs.m_detail) } {}

    JSONConstIterator::JSONConstIterator(const JSONConstIterator& rhs)
    : m_parent{ rhs.m_parent }
    , m_index{ rhs.m_index }
    , m_detail{ std::make_shared<detail::JSONConstIteratorDetail>(*rhs.m_detail) } {}

    JSONConstIterator::JSONConstIterator(const JSON* parent, JSONConstIterator::difference_type index, const detail::JSONConstIteratorDetail& d)
    : m_parent{ parent }
    , m_index{ index }
    , m_detail{ std::make_shared<detail::JSONConstIteratorDetail>(d.it) } {}

    JSONConstIterator& JSONConstIterator::operator =(const JSONIterator& rhs)
    {
    JSONConstIterator tmp{ rhs };

    this->m_parent = tmp.m_parent;
    this->m_index = tmp.m_index;
    this->m_detail = std::move(tmp.m_detail);

    return *this;
    }

    JSONConstIterator& JSONConstIterator::operator =(const JSONConstIterator& rhs)
    {
    JSONConstIterator tmp{ rhs };

    this->m_parent = tmp.m_parent;
    this->m_index = tmp.m_index;
    this->m_detail = std::move(tmp.m_detail);

    return *this;
    }

    JSONConstIterator& JSONConstIterator::operator ++()
    {
    ++m_detail->it;

    ++m_index;

    return *this;
    }

    JSONConstIterator JSONConstIterator::operator ++(int)
    {
    const JSONConstIterator tmp{ *this };

    ++(*this);

    return tmp;
    }

    JSONConstIterator& JSONConstIterator::operator --()
    {
    --m_detail->it;

    --m_index;

    return *this;
    }

    JSONConstIterator JSONConstIterator::operator --(int)
    {
    const JSONConstIterator tmp{ *this };

    --(*this);

    return tmp;
    }

    JSONConstIterator JSONConstIterator::operator +(JSONConstIterator::difference_type index) const
    {
    const detail::JSONConstIteratorDetail tmp{ m_detail->it + index };

    return JSONConstIterator{ m_parent, (m_index + index), tmp };
    }

    const JSONItem JSONConstIterator::operator *() const
    {
    return{ key(), value() };
    }

    String JSONConstIterator::key() const
    {
    if (m_parent != nullptr) SIV3D_LIKELY
    {
    switch (m_parent->getType())
    {
    case JSONValueType::Object:
    return Unicode::FromUTF8(m_detail->it.key());
    case JSONValueType::Array:
    return Format(m_index);
    default:
    return{};
    }
    }
    else SIV3D_UNLIKELY
    {
    throw Error{ U"This JSONConstIterator has not been constructed from any JSON." };
    }
    }

    const JSON JSONConstIterator::value() const
    {
    return JSON(std::make_shared<detail::JSONDetail>(detail::JSONDetail::Ref(), const_cast<nlohmann::json&>(*m_detail->it)));
    }

    bool JSONConstIterator::operator ==(const JSONConstIterator& other) const noexcept
    {
    if ((not m_detail) || (not other.m_detail))
    {
    return (not m_detail) && (not other.m_detail);
    }

    return (m_detail->it == other.m_detail->it);
    }

    bool JSONConstIterator::operator !=(const JSONConstIterator& other) const noexcept
    {
    return (not (*this == other));
    }

    //////////////////////////////////////////////////
    //
    // JSONIterationProxy
    //
    //////////////////////////////////////////////////

    JSONIterationProxy::JSONIterationProxy(const JSONIterationProxy& rhs)
    : m_detail{ std::make_shared<detail::JSONIterationProxyDetail>(*rhs.m_detail) } {}

    JSONIterationProxy::JSONIterationProxy(const detail::JSONIterationProxyDetail& d)
    : m_detail{ std::make_shared<detail::JSONIterationProxyDetail>(d.it) } {}

    JSONIterationProxy& JSONIterationProxy::operator =(const JSONIterationProxy& rhs)
    {
    JSONIterationProxy tmp = rhs;

    m_detail = std::move(tmp.m_detail);

    return *this;
    }

    JSONIterationProxy& JSONIterationProxy::operator++()
    {
    ++m_detail->it;

    return *this;
    }

    JSONIterationProxy JSONIterationProxy::operator++(int)
    {
    const detail::JSONIterationProxyDetail tmp{ m_detail->it };

    ++m_detail->it;

    return JSONIterationProxy{ tmp };
    }

    JSONIterationProxy JSONIterationProxy::operator+(size_t index) const
    {
    auto it = m_detail->it;

    std::advance(it, index);

    return JSONIterationProxy{ detail::JSONIterationProxyDetail{ it } };
    }

    JSON JSONIterationProxy::operator *() const
    {
    return JSON(std::make_shared<detail::JSONDetail>(detail::JSONDetail::Ref(), m_detail->it.value()));
    }

    bool JSONIterationProxy::operator ==(const JSONIterationProxy& other) const noexcept
    {
    return (m_detail->it == other.m_detail->it);
    }

    bool JSONIterationProxy::operator !=(const JSONIterationProxy& other) const noexcept
    {
    return (not (*this == other));
    }

    //////////////////////////////////////////////////
    //
    // JSONArrayView
    //
    //////////////////////////////////////////////////

    JSONArrayView::JSONArrayView(JSONIterationProxy begin, JSONIterationProxy end)
    : m_begin{ begin }
    , m_end{ end } {}

    JSONIterationProxy JSONArrayView::begin() const
    {
    return m_begin;
    }

    JSONIterationProxy JSONArrayView::end() const
    {
    return m_end;
    }

    JSON JSONArrayView::operator [](const size_t index) const
    {
    return *(m_begin + index);
    }

    //////////////////////////////////////////////////
    //
    // JSON
    //
    //////////////////////////////////////////////////

    JSON::JSON()
    : JSON(nullptr) {}

    JSON::JSON(Invalid_)
    : m_detail{ std::make_shared<detail::JSONDetail>() }
    , m_isValid{ false } {}

    JSON::JSON(std::nullptr_t)
    : m_detail{ std::make_shared<detail::JSONDetail>() } {}

    JSON::JSON(const JSON& other)
    : m_detail{ std::make_shared<detail::JSONDetail>(*other.m_detail) }
    , m_isValid{ other.m_isValid } {}

    JSON::JSON(JSON&& other) noexcept
    : m_detail{ std::exchange(other.m_detail, std::make_shared<detail::JSONDetail>()) }
    , m_isValid{ std::exchange(other.m_isValid, false) } {}

    JSON::JSON(std::shared_ptr<detail::JSONDetail>&& detail)
    : m_detail{ std::move(detail) } {}

    JSON::JSON(const std::initializer_list<std::pair<String, JSON>>& list)
    : m_detail{ std::make_shared<detail::JSONDetail>() }
    {
    operator =(list);
    }

    JSON::JSON(const Array<JSON>& array)
    : m_detail{ std::make_shared<detail::JSONDetail>() }
    {
    *this = array;
    }

    JSON::JSON(const StringView value)
    : m_detail{ std::make_shared<detail::JSONDetail>(detail::JSONDetail::Value(), nlohmann::json(Unicode::ToUTF8(value))) } {}

    JSON::JSON(const int64 value)
    : m_detail{ std::make_shared<detail::JSONDetail>(detail::JSONDetail::Value(), nlohmann::json(value)) } {}

    JSON::JSON(const uint64 value)
    : m_detail{ std::make_shared<detail::JSONDetail>(detail::JSONDetail::Value(), nlohmann::json(value)) } {}

    JSON::JSON(const double value)
    : m_detail{ std::make_shared<detail::JSONDetail>(detail::JSONDetail::Value(), nlohmann::json(value)) } {}

    JSON::JSON(const bool value)
    : m_detail{ std::make_shared<detail::JSONDetail>(detail::JSONDetail::Value(), nlohmann::json(value)) } {}

    JSON& JSON::operator =(std::nullptr_t)
    {
    m_detail->get() = nullptr;
    m_isValid = true;
    return *this;
    }

    JSON& JSON::operator =(const JSON& other)
    {
    m_detail = std::make_shared<detail::JSONDetail>(*other.m_detail);
    m_isValid = other.m_isValid;
    return *this;
    }

    JSON& JSON::operator =(JSON&& other) noexcept
    {
    m_detail = std::exchange(other.m_detail, std::make_shared<detail::JSONDetail>());
    m_isValid = std::exchange(other.m_isValid, false);
    return *this;
    }

    JSON& JSON::operator =(const std::initializer_list<std::pair<String, JSON>>& list)
    {
    m_detail->get().clear();

    for (const auto& element : list)
    {
    m_detail->get()[Unicode::ToUTF8(element.first)] = element.second.m_detail->get();
    }

    m_isValid = true;
    return *this;
    }

    JSON& JSON::operator =(const Array<JSON>& array)
    {
    m_detail->get() = nlohmann::json::array();

    for (const auto& element : array)
    {
    m_detail->get().push_back(element.m_detail->get());
    }

    m_isValid = true;
    return *this;
    }

    JSON& JSON::operator =(const StringView value)
    {
    m_detail->get() = Unicode::ToUTF8(value);
    m_isValid = true;
    return *this;
    }

    JSON& JSON::operator =(const int64 value)
    {
    m_detail->get() = value;
    m_isValid = true;
    return *this;
    }

    JSON& JSON::operator =(const uint64 value)
    {
    m_detail->get() = value;
    m_isValid = true;
    return *this;
    }

    JSON& JSON::operator =(const double value)
    {
    m_detail->get() = value;
    m_isValid = true;
    return *this;
    }

    JSON& JSON::operator =(const bool value)
    {
    m_detail->get() = value;
    m_isValid = true;
    return *this;
    }

    bool JSON::operator ==(const JSON& other) const
    {
    if (not m_isValid)
    {
    return (not other.m_isValid);
    }

    return (m_detail->get() == other.m_detail->get());
    }

    bool JSON::operator !=(const JSON& other) const
    {
    return !(*this == other);
    }

    bool JSON::isEmpty() const
    {
    return (not m_isValid);
    }

    JSON::operator bool() const
    {
    return m_isValid;
    }

    bool JSON::isNull() const
    {
    if (not m_isValid)
    {
    return false;
    }

    return m_detail->get().is_null();
    }

    bool JSON::isBool() const
    {
    if (not m_isValid)
    {
    return false;
    }

    return m_detail->get().is_boolean();
    }

    bool JSON::isNumber() const
    {
    if (not m_isValid)
    {
    return false;
    }

    return m_detail->get().is_number();
    }

    bool JSON::isInteger() const
    {
    if (not m_isValid)
    {
    return false;
    }

    return m_detail->get().is_number_integer();
    }

    bool JSON::isUnsigned() const
    {
    if (not m_isValid)
    {
    return false;
    }

    return m_detail->get().is_number_unsigned();
    }

    bool JSON::isFloat() const
    {
    if (not m_isValid)
    {
    return false;
    }

    return m_detail->get().is_number_float();
    }

    bool JSON::isString() const
    {
    if (not m_isValid)
    {
    return false;
    }

    return m_detail->get().is_string();
    }

    bool JSON::isArray() const
    {
    if (not m_isValid)
    {
    return false;
    }

    return m_detail->get().is_array();
    }

    bool JSON::isObject() const
    {
    if (not m_isValid)
    {
    return false;
    }

    return m_detail->get().is_object();
    }

    bool JSON::isBinary() const
    {
    if (not m_isValid)
    {
    return false;
    }

    return m_detail->get().is_binary();
    }

    JSONValueType JSON::getType() const
    {
    if (not m_isValid)
    {
    return JSONValueType::Empty;
    }

    const auto& j = m_detail->get();

    if (j.is_array())
    {
    return JSONValueType::Array;
    }
    else if (j.is_boolean())
    {
    return JSONValueType::Bool;
    }
    else if (j.is_null())
    {
    return JSONValueType::Null;
    }
    else if (j.is_number())
    {
    return JSONValueType::Number;
    }
    else if (j.is_object())
    {
    return JSONValueType::Object;
    }
    else if (j.is_string())
    {
    return JSONValueType::String;
    }
    else if (j.is_binary())
    {
    return JSONValueType::Binary;
    }

    throw Error{ U"JSON::getType(): Unknown JSONValueType" };
    }

    bool JSON::hasElement(const StringView name) const
    {
    if (not m_isValid)
    {
    return false;
    }

    return m_detail->get().contains(Unicode::ToUTF8(name));
    }

    String JSON::getString() const
    {
    if (not isString())
    {
    throw Error{ U"JSON::getString(): Value is not a String type" };
    }

    return Unicode::FromUTF8(m_detail->get().get<std::string>());
    }

    Array<uint8> JSON::getBinary() const
    {
    if (not isBinary())
    {
    throw Error{ U"JSON::getBinary(): Value is not a Binary type" };
    }

    return m_detail->get().get_binary();
    }

    JSON JSON::operator [](const StringView name)
    {
    if (not m_isValid)
    {
    return JSON::Invalid();
    }

    return JSON(std::make_shared<detail::JSONDetail>(
    detail::JSONDetail::Ref(), m_detail->get()[Unicode::ToUTF8(name)]));
    }

    const JSON JSON::operator [](const StringView name) const
    {
    if (not m_isValid)
    {
    return JSON::Invalid();
    }

    if (contains(name))
    {
    return JSON(std::make_shared<detail::JSONDetail>(detail::JSONDetail::Ref(), m_detail->get()[Unicode::ToUTF8(name)]));
    }

    throw Error{ U"JSON::operator [](StringView) const: Key `{}` not found"_fmt(name) };
    }

    JSON JSON::operator [](const size_t index)
    {
    if (not m_isValid)
    {
    return JSON::Invalid();
    }

    if (not isArray() && not isNull())
    {
    throw Error{ U"JSON::operator [](size_t): The value is neither of type Array nor of type Null." };
    }

    return JSON(std::make_shared<detail::JSONDetail>(detail::JSONDetail::Ref(), m_detail->get()[index]));
    }

    const JSON JSON::operator [](const size_t index) const
    {
    if (not m_isValid)
    {
    return JSON::Invalid();
    }

    if (not isArray())
    {
    throw Error{ U"JSON::operator [](size_t) const: The value is not an Array type" };
    }

    if (not contains(index))
    {
    throw Error{ U"JSON::operator [](size_t) const: Index out of range (index: {} >= size: {})"_fmt(index, size()) };
    }

    return JSON(std::make_shared<detail::JSONDetail>(detail::JSONDetail::Ref(), m_detail->get()[index]));
    }

    JSON JSON::operator[](const JSONPointer& jsonPointer)
    {
    return access(jsonPointer);
    }

    const JSON JSON::operator[](const JSONPointer& jsonPointer) const
    {
    return access(jsonPointer);
    }

    JSON JSON::access(const JSONPointer& jsonPointer)
    {
    if (not m_isValid)
    {
    return JSON::Invalid();
    }

    if (isObject() || isArray() || isNull())
    {
    return JSON(std::make_shared<detail::JSONDetail>(detail::JSONDetail::Ref(), m_detail->get()[jsonPointer.m_detail->pointer]));
    }

    throw Error{ U"JSON::access(JSONPointer): Invalid JSON type. This function requires that the value be of type Object, Array or Null." };
    }

    const JSON JSON::access(const JSONPointer& jsonPointer) const
    {
    if (not m_isValid)
    {
    return JSON::Invalid();
    }

    if (contains(jsonPointer))
    {
    return JSON(std::make_shared<detail::JSONDetail>(detail::JSONDetail::Ref(), m_detail->get()[jsonPointer.m_detail->pointer]));
    }

    throw Error{ U"JSON::access(JSONPointer) const: The element indicated by the JSON Pointer does not exist." };
    }

    JSON JSON::access(const StringView jsonPointer)
    {
    return access(JSONPointer{jsonPointer});
    }

    const JSON JSON::access(const StringView jsonPointer) const
    {
    return access(JSONPointer{jsonPointer});
    }

    void JSON::push_back(const JSON& value)
    {
    if (not m_isValid)
    {
    return;
    }

    m_detail->get().push_back(value.m_detail->get());
    }

    void JSON::clear() const
    {
    if (not m_isValid)
    {
    return;
    }

    m_detail->get().clear();
    }

    bool JSON::contains(StringView key) const noexcept
    {
    return isObject() && m_detail->get().find(Unicode::ToUTF8(key)) != m_detail->get().end();
    }

    bool JSON::contains(const size_t index) const noexcept
    {
    return isArray() && index < size();
    }

    bool JSON::contains(const JSONPointer& jsonPointer) const noexcept
    {
    return m_isValid && m_detail->get().contains(jsonPointer.m_detail->pointer);
    }

    void JSON::erase(const StringView name)
    {
    if (not m_isValid)
    {
    return;
    }

    m_detail->get().erase(Unicode::ToUTF8(name));
    }

    void JSON::erase(const size_t index)
    {
    if (not m_isValid)
    {
    return;
    }

    if (not isArray())
    {
    return;
    }

    m_detail->get().erase(index);
    }

    //JSON::iterator JSON::find(const StringView name)
    //{
    // const std::string key = Unicode::ToUTF8(name);

    // return iterator(detail::JSONIteratorDetail(m_detail->get().find(key)));
    //}

    //JSON::const_iterator JSON::find(const StringView name) const
    //{
    // const std::string key = Unicode::ToUTF8(name);

    // return const_iterator(detail::JSONConstIteratorDetail(m_detail->get().find(key)));
    //}

    JSON::iterator JSON::begin()
    {
    return iterator{ std::addressof(*this), 0, detail::JSONIteratorDetail(m_detail->get().begin()) };
    }

    JSON::const_iterator JSON::begin() const
    {
    return const_iterator{ std::addressof(*this), 0, detail::JSONConstIteratorDetail(m_detail->get().begin()) };
    }

    JSON::iterator JSON::end()
    {
    return iterator{ std::addressof(*this), static_cast<JSON::iterator::difference_type>(size()), detail::JSONIteratorDetail(m_detail->get().end()) };
    }

    JSON::const_iterator JSON::end() const
    {
    return const_iterator{ std::addressof(*this), static_cast<JSON::iterator::difference_type>(size()), detail::JSONConstIteratorDetail(m_detail->get().end()) };
    }

    JSONArrayView JSON::arrayView() const
    {
    if (not isArray())
    {
    throw Error{ U"JSON::arrayView(): Value is not an Array type" };
    }

    return JSONArrayView{
    JSONIterationProxy{ detail::JSONIterationProxyDetail(m_detail->get().items().begin()) },
    JSONIterationProxy{ detail::JSONIterationProxyDetail(m_detail->get().items().end()) }
    };
    }

    size_t JSON::size() const
    {
    if (not m_isValid)
    {
    return 0;
    }

    return m_detail->get().size();
    }

    String JSON::format(const char32 space, const size_t spaceCount) const
    {
    return Unicode::FromUTF8(formatUTF8(space, spaceCount));
    }

    String JSON::formatMinimum() const
    {
    return Unicode::FromUTF8(formatUTF8Minimum());
    }

    std::string JSON::formatUTF8(const char32 space, const size_t spaceCount) const
    {
    if (not m_isValid)
    {
    return{};
    }

    return m_detail->get().dump(static_cast<int>(spaceCount), static_cast<char>(space));
    }

    std::string JSON::formatUTF8Minimum() const
    {
    if (not m_isValid)
    {
    return{};
    }

    return m_detail->get().dump();
    }

    bool JSON::save(const FilePathView path) const
    {
    if (not m_isValid)
    {
    return false;
    }

    TextWriter writer{ path };

    if (not writer)
    {
    return false;
    }

    writer.write(format());

    return true;
    }

    bool JSON::saveMinimum(const FilePathView path) const
    {
    if (not m_isValid)
    {
    return false;
    }

    TextWriter writer{ path };

    if (not writer)
    {
    return false;
    }

    writer.write(formatMinimum());

    return true;
    }

    Blob JSON::toBSON() const
    {
    std::vector<uint8> result;
    nlohmann::json::to_bson(m_detail->get(), result);
    return Blob{ result.data(), result.size() };
    }

    Blob JSON::toCBOR() const
    {
    std::vector<uint8> result;
    nlohmann::json::to_cbor(m_detail->get(), result);
    return Blob{ result.data(), result.size() };
    }

    Blob JSON::toMessagePack() const
    {
    std::vector<uint8> result;
    nlohmann::json::to_msgpack(m_detail->get(), result);
    return Blob{ result.data(), result.size() };
    }

    void JSON::swap(JSON& other) noexcept
    {
    m_detail.swap(other.m_detail);
    std::swap(m_isValid, other.m_isValid);
    }

    JSON JSON::Invalid()
    {
    return JSON(Invalid_{});
    }

    JSON JSON::Load(const FilePathView path, const AllowExceptions allowExceptions)
    {
    TextReader reader{ path };

    if (not reader)
    {
    if (allowExceptions)
    {
    throw Error{ U"JSON::load(): failed to open `{}`"_fmt(path) };
    }

    return JSON::Invalid();
    }

    return Parse(reader.readAll(), allowExceptions);
    }

    JSON JSON::Load(std::unique_ptr<IReader>&& reader, const AllowExceptions allowExceptions)
    {
    TextReader textReader{ std::move(reader) };

    if (not textReader)
    {
    if (allowExceptions)
    {
    throw Error(U"JSON::load(): failed to open from IReader");
    }

    return JSON::Invalid();
    }

    return Parse(textReader.readAll(), allowExceptions);
    }

    JSON JSON::Parse(const StringView str, const AllowExceptions allowExceptions)
    {
    JSON value{ Invalid_{} };

    try
    {
    value.m_detail = std::make_shared<detail::JSONDetail>(detail::JSONDetail::Value(), nlohmann::json::parse(Unicode::ToUTF8(str)));
    value.m_isValid = true;
    }
    catch (const std::exception& e)
    {
    if (not allowExceptions)
    {
    return JSON::Invalid();
    }

    throw Error{ U"JSON::Parse(): " + Unicode::Widen(e.what()) };
    }

    return value;
    }

    JSON JSON::FromBSON(const Blob& bson, const AllowExceptions allowExceptions)
    {
    JSON value{ Invalid_{} };

    try
    {
    value.m_detail = std::make_shared<detail::JSONDetail>(detail::JSONDetail::Value(), nlohmann::json::from_bson(bson.begin(), bson.end(), true, allowExceptions.getBool()));
    value.m_isValid = true;
    }
    catch (const std::exception& e)
    {
    if (not allowExceptions)
    {
    return JSON::Invalid();
    }

    throw Error{ U"JSON::FromBSON(): " + Unicode::Widen(e.what()) };
    }

    return value;
    }

    JSON JSON::FromCBOR(const Blob& cbor, const AllowExceptions allowExceptions)
    {
    JSON value{ Invalid_{} };

    try
    {
    value.m_detail = std::make_shared<detail::JSONDetail>(detail::JSONDetail::Value(), nlohmann::json::from_cbor(cbor.begin(), cbor.end(), true, allowExceptions.getBool()));
    value.m_isValid = true;
    }
    catch (const std::exception& e)
    {
    if (not allowExceptions)
    {
    return JSON::Invalid();
    }

    throw Error{ U"JSON::FromCBOR(): " + Unicode::Widen(e.what()) };
    }

    return value;
    }

    JSON JSON::FromMessagePack(const Blob& msgpack, const AllowExceptions allowExceptions)
    {
    JSON value{ Invalid_{} };

    try
    {
    value.m_detail = std::make_shared<detail::JSONDetail>(detail::JSONDetail::Value(), nlohmann::json::from_msgpack(msgpack.begin(), msgpack.end(), true, allowExceptions.getBool()));
    value.m_isValid = true;
    }
    catch (const std::exception& e)
    {
    if (not allowExceptions)
    {
    return JSON::Invalid();
    }

    throw Error{ U"JSON::FromMessagePack(): " + Unicode::Widen(e.what()) };
    }

    return value;
    }

    void Formatter(FormatData& formatData, const JSON& value)
    {
    Formatter(formatData, value.format());
    }

    Optional<String> JSON::getOptString() const
    {
    if (not isString())
    {
    return none;
    }

    return Unicode::FromUTF8(m_detail->get().get<std::string>());
    }

    Optional<int64> JSON::getOptInt64() const
    {
    if (not isNumber())
    {
    return none;
    }

    if (isBool())
    {
    return static_cast<int64>(m_detail->get().get<bool>());
    }

    return m_detail->get().get<int64>();
    }

    Optional<double> JSON::getOptDouble() const
    {
    if (not isNumber())
    {
    return none;
    }

    return m_detail->get().get<double>();
    }

    Optional<bool> JSON::getOptBool() const
    {
    if (not isBool())
    {
    return none;
    }

    return m_detail->get().get<bool>();
    }

    //////////////////////////////////////////////////
    //
    // JSONPointer
    //
    //////////////////////////////////////////////////

    JSONPointer::JSONPointer(StringView jsonPointer)
    : m_detail()
    {
    if (not jsonPointer.empty() && not jsonPointer.starts_with(U"/"))
    {
    throw Error{U"JSONPointer::(constructor): JSON pointers must start with \"/\" or be empty."};
    }

    try
    {
    m_detail = std::make_shared<detail::JSONPointerDetail>(Unicode::ToUTF8(jsonPointer));
    }
    catch (const std::exception&)
    {
    throw Error{U"JSONPointer::(constructor): JSON pointers must not contain only \"~\" which is not followed by 0 or 1. \
    This is because, in a json pointer, \"~\" is escaped to \"~0\" and \"/\" is escaped to \"~1\", as \"/\" is used as a delimiter. \
    In push_back, however, JSON keys are passed as is without escaping."};
    }
    }

    JSONPointer::JSONPointer(const JSONPointer& other)
    : m_detail(std::make_shared<detail::JSONPointerDetail>(other.m_detail->pointer))
    {}

    JSONPointer::JSONPointer(std::shared_ptr<detail::JSONPointerDetail>&& detail)
    : m_detail(std::move(detail))
    {}

    JSONPointer& JSONPointer::operator =(const JSONPointer& other)
    {
    JSONPointer tmp{ other };

    this->m_detail = std::move(tmp).m_detail;

    return *this;
    }

    String JSONPointer::format() const
    {
    return Unicode::FromUTF8(m_detail->pointer.to_string());
    }

    bool operator ==(const JSONPointer& lhs, const JSONPointer& rhs) noexcept
    {
    return lhs.m_detail->pointer == rhs.m_detail->pointer;
    }

    JSONPointer& JSONPointer::operator /=(const JSONPointer& rhs)
    {
    this->m_detail->pointer /= rhs.m_detail->pointer;

    return *this;
    }

    JSONPointer& JSONPointer::operator /=(StringView unescapedToken)
    {
    this->m_detail->pointer /= Unicode::ToUTF8(unescapedToken);

    return *this;
    }

    JSONPointer& JSONPointer::operator /=(size_t index)
    {
    this->m_detail->pointer /= index;

    return *this;
    }

    JSONPointer JSONPointer::operator /(const JSONPointer& rhs) const
    {
    JSONPointer tmp{ *this };

    return tmp /= rhs;
    }

    JSONPointer JSONPointer::operator /(StringView unescapedToken) const
    {
    JSONPointer tmp{ *this };

    return tmp /= unescapedToken;
    }

    JSONPointer JSONPointer::operator /(size_t index) const
    {
    JSONPointer tmp{ *this };

    return tmp /= index;
    }

    JSONPointer JSONPointer::parent() const
    {
    if (empty())
    {
    throw Error{U"JSONPointer::parent(): JSON pointer indicates root document, therefore has no parent."};
    }

    return JSONPointer{ std::make_shared<detail::JSONPointerDetail>(m_detail->pointer.parent_pointer()) };
    }

    String JSONPointer::back() const
    {
    if (empty())
    {
    throw Error{U"JSONPointer::back(): JSON pointer indicates root document, therefore has no parent."};
    }

    return Unicode::FromUTF8(m_detail->pointer.back());
    }

    void JSONPointer::pop_back()
    {
    if (empty())
    {
    throw Error{U"JSONPointer::pop_back(): JSON pointer indicates root document, therefore has no parent."};
    }

    m_detail->pointer.pop_back();
    }

    void JSONPointer::pop_back_N(size_t n)
    {
    for (size_t i = 0; i < n && (not empty()); ++i)
    {
    m_detail->pointer.pop_back();
    }
    }

    void JSONPointer::push_back(const JSONPointer& rhs)
    {
    *this /= rhs;
    }

    void JSONPointer::push_back(StringView unescapedToken)
    {
    *this /= unescapedToken;
    }

    void JSONPointer::push_back(size_t index)
    {
    *this /= index;
    }

    bool JSONPointer::isEmpty() const noexcept
    {
    return m_detail->pointer.empty();
    }

    bool JSONPointer::empty() const noexcept
    {
    return m_detail->pointer.empty();
    }

    void Formatter(FormatData& formatData, const JSONPointer& value)
    {
    formatData.string += value.format();
    }

    String JSONPointer::Escape(StringView unescapedToken)
    {
    return Unicode::FromUTF8(nlohmann::detail::escape(Unicode::ToUTF8(unescapedToken)));
    }

    String JSONPointer::Unescape(StringView escapedToken)
    {
    std::string tmp{ Unicode::ToUTF8(escapedToken) };

    nlohmann::detail::unescape(tmp);

    return Unicode::FromUTF8(tmp);
    }

    //////////////////////////////////////////////////
    //
    // JSONValidator
    //
    //////////////////////////////////////////////////

    JSONValidator::JSONValidator()
    : m_detail{ std::make_shared<detail::JSONValidatorDetail>() }
    {}

    JSONValidator::JSONValidator(Invalid_)
    : m_detail{ std::make_shared<detail::JSONValidatorDetail>() }
    , m_isValid{ false }
    {}

    JSONValidator::JSONValidator(std::shared_ptr<detail::JSONValidatorDetail>&& detail)
    : m_detail(std::move(detail))
    {}

    bool JSONValidator::isEmpty() const noexcept
    {
    return (not m_isValid);
    }

    bool JSONValidator::validate(const JSON& json) const noexcept
    {
    detail::ValidationErrorHandler err;

    m_detail->validator.validate(json.m_detail.get()->get(), err);

    return err.details.isOK();
    }

    bool JSONValidator::validate(const JSON& json, ValidationError& status) const noexcept
    {
    detail::ValidationErrorHandler err;

    m_detail->validator.validate(json.m_detail.get()->get(), err);

    status = std::move(err).details;

    return status.isOK();
    }

    void JSONValidator::validationAssert(const JSON& json) const
    {
    detail::ValidationErrorHandler err;

    m_detail->validator.validate(json.m_detail.get()->get(), err);

    if (err.details.isError())
    {
    throw std::move(err).details;
    }
    }

    JSONValidator JSONValidator::Invalid()
    {
    return JSONValidator{ JSONValidator::Invalid_{} };
    }

    JSONValidator JSONValidator::Load(FilePathView path, AllowExceptions allowExceptions)
    {
    return JSONValidator::Set(JSON::Load(path, allowExceptions), allowExceptions);
    }

    JSONValidator JSONValidator::Load(std::unique_ptr<IReader>&& reader, AllowExceptions allowExceptions)
    {
    return JSONValidator::Set(JSON::Load(std::move(reader), allowExceptions), allowExceptions);
    }

    JSONValidator JSONValidator::Parse(StringView str, AllowExceptions allowExceptions)
    {
    return JSONValidator::Set(JSON::Parse(str, allowExceptions), allowExceptions);
    }

    JSONValidator JSONValidator::Set(const JSON& schema, AllowExceptions allowExceptions)
    {
    try
    {
    nlohmann::json_schema::json_validator validator;

    validator.set_root_schema(schema.m_detail.get()->get());

    return JSONValidator{ std::make_shared<detail::JSONValidatorDetail>(std::move(validator)) };
    }
    catch (const std::exception& e)
    {
    if (not allowExceptions)
    {
    return JSONValidator::Invalid();
    }

    throw Error{ U"JSONValidator::Load/Parse/Set(): " + Unicode::Widen(e.what()) };
    }
    }

    //////////////////////////////////////////////////
    //
    // JSONSchema::ValidationError
    //
    //////////////////////////////////////////////////

    JSONValidator::ValidationError::ValidationError(StringView message, const JSONPointer& pointer, const JSON& instance)
    : Error(message)
    , m_pointer(pointer)
    , m_instance(instance)
    {}

    bool JSONValidator::ValidationError::isOK() const noexcept
    {
    return m_what.empty();
    }

    bool JSONValidator::ValidationError::isError() const noexcept
    {
    return (not isOK());
    }

    JSONValidator::ValidationError::operator bool() const noexcept
    {
    return isOK();
    }

    void JSONValidator::ValidationError::reset()
    {
    m_what.clear();
    m_pointer = JSONPointer{};
    m_instance.clear();
    }

    const String& JSONValidator::ValidationError::message() const noexcept
    {
    return m_what;
    }

    const JSONPointer& JSONValidator::ValidationError::pointer() const noexcept
    {
    return m_pointer;
    }

    const JSON& JSONValidator::ValidationError::instance() const noexcept
    {
    return m_instance;
    }

    StringView JSONValidator::ValidationError::type() const noexcept
    {
    return StringView{ U"JSONValidator::ValidationError" };
    }

    std::ostream& operator<<(std::ostream& output, const JSONValidator::ValidationError& value)
    {
    return (output << Format(value).narrow());
    }

    std::wostream& operator<<(std::wostream& output, const JSONValidator::ValidationError& value)
    {
    return (output << Format(value).toWstr());
    }

    std::basic_ostream<char32>& operator<<(std::basic_ostream<char32>& output, const JSONValidator::ValidationError& value)
    {
    return (output << Format(value));
    }

    void Formatter(FormatData& formatData, const JSONValidator::ValidationError& value)
    {
    // pointer 及び instance は表示させない
    Formatter(formatData, static_cast<const Error&>(value));
    }
    }