From 1d055261b4144dbf86b2658437015b15d4dd9bff Mon Sep 17 00:00:00 2001
From: Richard <q@1bpm.net>
Date: Sun, 4 Sep 2022 00:32:56 +0100
Subject: initial

---
 include/jsoncons_ext/jsonpointer/jsonpointer.hpp   | 1577 ++++++++++++++++++++
 .../jsoncons_ext/jsonpointer/jsonpointer_error.hpp |  119 ++
 2 files changed, 1696 insertions(+)
 create mode 100644 include/jsoncons_ext/jsonpointer/jsonpointer.hpp
 create mode 100644 include/jsoncons_ext/jsonpointer/jsonpointer_error.hpp

(limited to 'include/jsoncons_ext/jsonpointer')

diff --git a/include/jsoncons_ext/jsonpointer/jsonpointer.hpp b/include/jsoncons_ext/jsonpointer/jsonpointer.hpp
new file mode 100644
index 0000000..41e41e2
--- /dev/null
+++ b/include/jsoncons_ext/jsonpointer/jsonpointer.hpp
@@ -0,0 +1,1577 @@
+// Copyright 2017 Daniel Parker
+// Distributed under the Boost license, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_JSONPOINTER_JSONPOINTER_HPP
+#define JSONCONS_JSONPOINTER_JSONPOINTER_HPP
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <iostream>
+#include <iterator>
+#include <utility> // std::move
+#include <system_error> // system_error
+#include <type_traits> // std::enable_if, std::true_type
+#include <jsoncons/json.hpp>
+#include <jsoncons_ext/jsonpointer/jsonpointer_error.hpp>
+#include <jsoncons/detail/write_number.hpp>
+
+namespace jsoncons { namespace jsonpointer {
+
+    namespace detail {
+
+    enum class pointer_state 
+    {
+        start,
+        escaped,
+        delim
+    };
+
+    } // namespace detail
+
+    template <class CharT>
+    std::basic_string<CharT> escape_string(const std::basic_string<CharT>& s)
+    {
+        std::basic_string<CharT> result;
+        for (auto c : s)
+        {
+            switch (c)
+            {
+                case '~':
+                    result.push_back('~');
+                    result.push_back('0');
+                    break;
+                case '/':
+                    result.push_back('~');
+                    result.push_back('1');
+                    break;
+                default:
+                    result.push_back(c);
+                    break;
+            }
+        }
+        return result;
+    }
+
+    // basic_json_pointer
+
+    template <class CharT>
+    class basic_json_pointer
+    {
+    public:
+        // Member types
+        using char_type = CharT;
+        using string_type = std::basic_string<char_type>;
+        using string_view_type = jsoncons::basic_string_view<char_type>;
+        using const_iterator = typename std::vector<string_type>::const_iterator;
+        using iterator = const_iterator;
+        using const_reverse_iterator = typename std::vector<string_type>::const_reverse_iterator;
+        using reverse_iterator = const_reverse_iterator;
+    private:
+        std::vector<string_type> tokens_;
+    public:
+        // Constructors
+        basic_json_pointer()
+        {
+        }
+
+        basic_json_pointer(const std::vector<string_type>& tokens)
+            : tokens_(tokens)
+        {
+        }
+
+        basic_json_pointer(std::vector<string_type>&& tokens)
+            : tokens_(std::move(tokens))
+        {
+        }
+
+        explicit basic_json_pointer(const string_view_type& s)
+        {
+            std::error_code ec;
+            auto jp = parse(s, ec);
+            if (ec)
+            {
+                throw jsonpointer_error(ec);
+            }
+            tokens_ = std::move(jp.tokens_);
+        }
+
+        explicit basic_json_pointer(const string_view_type& s, std::error_code& ec)
+        {
+            auto jp = parse(s, ec);
+            if (!ec)
+            {
+                tokens_ = std::move(jp.tokens_);
+            }
+        }
+
+        basic_json_pointer(const basic_json_pointer&) = default;
+
+        basic_json_pointer(basic_json_pointer&&) = default;
+
+        static basic_json_pointer parse(const string_view_type& input, std::error_code& ec)
+        {
+            std::vector<string_type> tokens;
+            if (input.empty() || (input[0] == '#' && input.size() == 1))
+            {
+                return basic_json_pointer<CharT>();
+            }
+
+            const char_type* p;
+            const char_type* pend;
+            string_type unescaped;
+            if (input[0] == '#') 
+            {
+                unescaped = unescape_uri_string(input, ec);
+                p = unescaped.data() + 1;
+                pend = unescaped.data() + unescaped.size();
+            }
+            else
+            {
+                p = input.data();
+                pend = input.data() + input.size();
+            }
+
+            auto state = jsonpointer::detail::pointer_state::start;
+            string_type buffer;
+
+            while (p < pend)
+            {
+                bool done = false;
+                while (p < pend && !done)
+                {
+                    switch (state)
+                    {
+                        case jsonpointer::detail::pointer_state::start: 
+                            switch (*p)
+                            {
+                                case '/':
+                                    state = jsonpointer::detail::pointer_state::delim;
+                                    break;
+                                default:
+                                    ec = jsonpointer_errc::expected_slash;
+                                    return basic_json_pointer();
+                            };
+                            break;
+                        case jsonpointer::detail::pointer_state::delim: 
+                            switch (*p)
+                            {
+                                case '/':
+                                    done = true;
+                                    break;
+                                case '~':
+                                    state = jsonpointer::detail::pointer_state::escaped;
+                                    break;
+                                default:
+                                    buffer.push_back(*p);
+                                    break;
+                            };
+                            break;
+                        case jsonpointer::detail::pointer_state::escaped: 
+                            switch (*p)
+                            {
+                                case '0':
+                                    buffer.push_back('~');
+                                    state = jsonpointer::detail::pointer_state::delim;
+                                    break;
+                                case '1':
+                                    buffer.push_back('/');
+                                    state = jsonpointer::detail::pointer_state::delim;
+                                    break;
+                                default:
+                                    ec = jsonpointer_errc::expected_0_or_1;
+                                    return basic_json_pointer();
+                            };
+                            break;
+                    }
+                    ++p;
+                }
+                tokens.push_back(buffer);
+                buffer.clear();
+            }
+            if (!buffer.empty())
+            {
+                tokens.push_back(buffer);
+            }
+            return basic_json_pointer(tokens);
+        }
+
+        static string_type escape_uri_string(const string_type& s)
+        {
+            string_type escaped;
+            for (auto ch : s)
+            {
+                switch (ch)
+                {
+                    case '%':
+                        escaped.append(string_type{'%','2','5'});
+                        break;
+                    case '^':
+                        escaped.append(string_type{'%','5','E'});
+                        break;
+                    case '|':
+                        escaped.append(string_type{'%','7','C'});
+                        break;
+                    case '\\':
+                        escaped.append(string_type{'%','5','C'});
+                        break;
+                    case '\"':
+                        escaped.append(string_type{'%','2','2'});
+                        break;
+                    case ' ':
+                        escaped.append(string_type{'%','2','0'});
+                        break;
+                    default:
+                        escaped.push_back(ch);
+                        break;
+                }
+            }
+
+            return escaped;
+        }
+
+        static string_type unescape_uri_string(const string_view_type& s, std::error_code& ec)
+        {
+            if (s.size() < 3)
+            {
+                return string_type(s);
+            }
+            string_type unescaped;
+            std::size_t last = s.size() - 2;
+            std::size_t pos = 0;
+            while (pos < last)
+            {
+                if (s[pos] == '%')
+                {
+                    uint8_t ch;
+                    auto result = jsoncons::detail::to_integer_base16(s.data() + (pos+1), 2, ch);
+                    if (!result)
+                    {
+                        ec = jsonpointer_errc::invalid_uri_escaped_data;
+                        return string_type(s);
+                    }
+                    unescaped.push_back(ch);
+                    pos += 3;
+                }
+                else
+                {
+                    unescaped.push_back(s[pos]);
+                    ++pos;
+                }
+            }
+            while (pos < s.size())
+            {
+                unescaped.push_back(s[pos]);
+                ++pos;
+            }
+            return unescaped;
+        }
+
+        // operator=
+        basic_json_pointer& operator=(const basic_json_pointer&) = default;
+
+        basic_json_pointer& operator=(basic_json_pointer&&) = default;
+
+        // Modifiers
+
+        void clear()
+        {
+            tokens_.clear();
+        }
+
+        basic_json_pointer& operator/=(const string_type& s) 
+        {
+            tokens_.push_back(s);
+            return *this;
+        }
+
+        template <class IntegerType>
+        typename std::enable_if<type_traits::is_integer<IntegerType>::value, basic_json_pointer&>::type
+        operator/=(IntegerType val)
+        {
+            string_type s;
+            jsoncons::detail::from_integer(val, s);
+            tokens_.push_back(s);
+
+            return *this;
+        }
+
+        basic_json_pointer& operator+=(const basic_json_pointer& p)
+        {
+            for (const auto& s : p.tokens_)
+            {
+                tokens_.push_back(s);
+            }
+            return *this;
+        }
+
+        // Accessors
+        bool empty() const
+        {
+          return tokens_.empty();
+        }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+
+        JSONCONS_DEPRECATED_MSG("Instead, use to_string()")
+        string_type string() const
+        {
+            return to_string();
+        }
+#endif
+        string_type to_string() const
+        {
+            string_type buffer;
+            for (const auto& token : tokens_)
+            {
+                buffer.push_back('/');
+                for (auto c : token)
+                {
+                    switch (c)
+                    {
+                        case '~':
+                            buffer.push_back('~');
+                            buffer.push_back('0');
+                            break;
+                        case '/':
+                            buffer.push_back('~');
+                            buffer.push_back('1');
+                            break;
+                        default:
+                            buffer.push_back(c);
+                            break;
+                    }
+                }
+            }
+            return buffer;
+        }
+
+        string_type to_uri_fragment() const
+        {
+            string_type buffer{'#'};
+            for (const auto& token : tokens_)
+            {
+                buffer.push_back('/');
+                string_type s = escape_uri_string(token);
+                for (auto c : s)
+                {
+                    switch (c)
+                    {
+                        case '~':
+                            buffer.push_back('~');
+                            buffer.push_back('0');
+                            break;
+                        case '/':
+                            buffer.push_back('~');
+                            buffer.push_back('1');
+                            break;
+                        default:
+                            buffer.push_back(c);
+                            break;
+                    }
+                }
+            }
+            return buffer;
+        }
+
+        // Iterators
+        iterator begin() const
+        {
+            return tokens_.begin();
+        }
+        iterator end() const
+        {
+            return tokens_.end();
+        }
+
+        reverse_iterator rbegin() const
+        {
+            return tokens_.rbegin();
+        }
+        reverse_iterator rend() const
+        {
+            return tokens_.rend();
+        }
+
+        // Non-member functions
+        friend basic_json_pointer<CharT> operator/(const basic_json_pointer<CharT>& lhs, const string_type& rhs)
+        {
+            basic_json_pointer<CharT> p(lhs);
+            p /= rhs;
+            return p;
+        }
+
+        friend basic_json_pointer<CharT> operator+( const basic_json_pointer<CharT>& lhs, const basic_json_pointer<CharT>& rhs )
+        {
+            basic_json_pointer<CharT> p(lhs);
+            p += rhs;
+            return p;
+        }
+
+        friend bool operator==( const basic_json_pointer& lhs, const basic_json_pointer& rhs )
+        {
+            return lhs.tokens_ == rhs.okens_;
+        }
+
+        friend bool operator!=( const basic_json_pointer& lhs, const basic_json_pointer& rhs )
+        {
+            return lhs.tokens_ != rhs.tokens_;
+        }
+
+        friend std::basic_ostream<CharT>&
+        operator<<( std::basic_ostream<CharT>& os, const basic_json_pointer<CharT>& p )
+        {
+            os << p.to_string();
+            return os;
+        }
+    };
+
+    template <class CharT,class IntegerType>
+    typename std::enable_if<type_traits::is_integer<IntegerType>::value, basic_json_pointer<CharT>>::type
+    operator/(const basic_json_pointer<CharT>& lhs, IntegerType rhs)
+    {
+        basic_json_pointer<CharT> p(lhs);
+        p /= rhs;
+        return p;
+    }
+
+    using json_pointer = basic_json_pointer<char>;
+    using wjson_pointer = basic_json_pointer<wchar_t>;
+
+    #if !defined(JSONCONS_NO_DEPRECATED)
+    template<class CharT>
+    using basic_address = basic_json_pointer<CharT>;
+    template<class CharT>
+    using basic_json_ptr = basic_json_pointer<CharT>;
+    JSONCONS_DEPRECATED_MSG("Instead, use json_pointer") typedef json_pointer address;
+    JSONCONS_DEPRECATED_MSG("Instead, use json_pointer") typedef json_pointer json_ptr;
+    JSONCONS_DEPRECATED_MSG("Instead, use wjson_pointer") typedef json_pointer wjson_ptr;
+    #endif
+
+    namespace detail {
+
+    template <class Json>
+    const Json* resolve(const Json* current, const typename Json::string_view_type& buffer, std::error_code& ec)
+    {
+        if (current->is_array())
+        {
+            if (buffer.size() == 1 && buffer[0] == '-')
+            {
+                ec = jsonpointer_errc::index_exceeds_array_size;
+                return current;
+            }
+            std::size_t index{0};
+            auto result = jsoncons::detail::to_integer_decimal(buffer.data(), buffer.length(), index);
+            if (!result)
+            {
+                ec = jsonpointer_errc::invalid_index;
+                return current;
+            }
+            if (index >= current->size())
+            {
+                ec = jsonpointer_errc::index_exceeds_array_size;
+                return current;
+            }
+            current = std::addressof(current->at(index));
+        }
+        else if (current->is_object())
+        {
+            if (!current->contains(buffer))
+            {
+                ec = jsonpointer_errc::key_not_found;
+                return current;
+            }
+            current = std::addressof(current->at(buffer));
+        }
+        else
+        {
+            ec = jsonpointer_errc::expected_object_or_array;
+            return current;
+        }
+        return current;
+    }
+
+    template <class Json>
+    Json* resolve(Json* current, const typename Json::string_view_type& buffer, bool create_if_missing, std::error_code& ec)
+    {
+        if (current->is_array())
+        {
+            if (buffer.size() == 1 && buffer[0] == '-')
+            {
+                ec = jsonpointer_errc::index_exceeds_array_size;
+                return current;
+            }
+            std::size_t index{0};
+            auto result = jsoncons::detail::to_integer_decimal(buffer.data(), buffer.length(), index);
+            if (!result)
+            {
+                ec = jsonpointer_errc::invalid_index;
+                return current;
+            }
+            if (index >= current->size())
+            {
+                ec = jsonpointer_errc::index_exceeds_array_size;
+                return current;
+            }
+            current = std::addressof(current->at(index));
+        }
+        else if (current->is_object())
+        {
+            if (!current->contains(buffer))
+            {
+                if (create_if_missing)
+                {
+                    auto r = current->try_emplace(buffer, Json());
+                    current = std::addressof(r.first->value());
+                }
+                else
+                {
+                    ec = jsonpointer_errc::key_not_found;
+                    return current;
+                }
+            }
+            else
+            {
+                current = std::addressof(current->at(buffer));
+            }
+        }
+        else
+        {
+            ec = jsonpointer_errc::expected_object_or_array;
+            return current;
+        }
+        return current;
+    }
+
+    } // namespace detail
+
+    // get
+
+    template<class Json>
+    Json& get(Json& root, 
+              const basic_json_pointer<typename Json::char_type>& location, 
+              bool create_if_missing,
+              std::error_code& ec)
+    {
+        if (location.empty())
+        {
+            return root;
+        }
+
+        Json* current = std::addressof(root);
+        auto it = location.begin();
+        auto end = location.end();
+        while (it != end)
+        {
+            current = jsoncons::jsonpointer::detail::resolve(current, *it, create_if_missing, ec);
+            if (ec)
+                return *current;
+            ++it;
+        }
+        return *current;
+    }
+
+    template<class Json, class StringSource>
+    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,Json&>::type
+    get(Json& root, 
+        const StringSource& location_str, 
+        bool create_if_missing,
+        std::error_code& ec)
+    {
+        auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec);
+        if (ec)
+        {
+            return root;
+        }
+        return get(root, jsonptr, create_if_missing, ec);
+    }
+
+    template<class Json>
+    const Json& get(const Json& root, 
+                    const basic_json_pointer<typename Json::char_type>& location, 
+                    std::error_code& ec)
+    {
+        if (location.empty())
+        {
+            return root;
+        }
+
+        const Json* current = std::addressof(root);
+        auto it = location.begin();
+        auto end = location.end();
+        while (it != end)
+        {
+            current = jsoncons::jsonpointer::detail::resolve(current, *it, ec);
+            if (ec)
+                return *current;
+            ++it;
+        }
+        return *current;
+    }
+
+    template<class Json, class StringSource>
+    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,const Json&>::type
+    get(const Json& root, 
+        const StringSource& location_str, 
+        std::error_code& ec)
+    {
+        auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec);
+        if (ec)
+        {
+            return root;
+        }
+        return get(root, jsonptr, ec);
+    }
+
+    template<class Json>
+    Json& get(Json& root, 
+              const basic_json_pointer<typename Json::char_type>& location, 
+              std::error_code& ec)
+    {
+        return get(root, location, false, ec);
+    }
+
+    template<class Json, class StringSource>
+    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,Json&>::type
+    get(Json& root, 
+        const StringSource& location_str, 
+        std::error_code& ec)
+    {
+        return get(root, location_str, false, ec);
+    }
+
+    template<class Json>
+    Json& get(Json& root, 
+              const basic_json_pointer<typename Json::char_type>& location,
+              bool create_if_missing = false)
+    {
+        std::error_code ec;
+        Json& j = get(root, location, create_if_missing, ec);
+        if (ec)
+        {
+            JSONCONS_THROW(jsonpointer_error(ec));
+        }
+        return j;
+    }
+
+    template<class Json, class StringSource>
+    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,Json&>::type
+    get(Json& root, 
+              const StringSource& location_str,
+              bool create_if_missing = false)
+    {
+        std::error_code ec;
+        Json& result = get(root, location_str, create_if_missing, ec);
+        if (ec)
+        {
+            JSONCONS_THROW(jsonpointer_error(ec));
+        }
+        return result;
+    }
+
+    template<class Json>
+    const Json& get(const Json& root, const basic_json_pointer<typename Json::char_type>& location)
+    {
+        std::error_code ec;
+        const Json& j = get(root, location, ec);
+        if (ec)
+        {
+            JSONCONS_THROW(jsonpointer_error(ec));
+        }
+        return j;
+    }
+
+    template<class Json, class StringSource>
+    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,const Json&>::type
+    get(const Json& root, const StringSource& location_str)
+    {
+        std::error_code ec;
+        const Json& j = get(root, location_str, ec);
+        if (ec)
+        {
+            JSONCONS_THROW(jsonpointer_error(ec));
+        }
+        return j;
+    }
+
+    // contains
+
+    template<class Json>
+    bool contains(const Json& root, const basic_json_pointer<typename Json::char_type>& location)
+    {
+        std::error_code ec;
+        get(root, location, ec);
+        return !ec ? true : false;
+    }
+
+    template<class Json, class StringSource>
+    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,bool>::type
+    contains(const Json& root, const StringSource& location_str)
+    {
+        std::error_code ec;
+        get(root, location_str, ec);
+        return !ec ? true : false;
+    }
+
+    template<class Json,class T>
+    void add(Json& root, 
+             const basic_json_pointer<typename Json::char_type>& location, 
+             T&& value, 
+             bool create_if_missing,
+             std::error_code& ec)
+    {
+        Json* current = std::addressof(root);
+
+        std::basic_string<typename Json::char_type> buffer;
+        auto it = location.begin();
+        auto end = location.end();
+        while (it != end)
+        {
+            buffer = *it;
+            ++it;
+            if (it != end)
+            {
+                current = jsoncons::jsonpointer::detail::resolve(current, buffer, create_if_missing, ec);
+                if (ec)
+                    return;
+            }
+        }
+        if (current->is_array())
+        {
+            if (buffer.size() == 1 && buffer[0] == '-')
+            {
+                current->emplace_back(std::forward<T>(value));
+                current = std::addressof(current->at(current->size()-1));
+            }
+            else
+            {
+                std::size_t index{0};
+                auto result = jsoncons::detail::to_integer_decimal(buffer.data(), buffer.length(), index);
+                if (!result)
+                {
+                    ec = jsonpointer_errc::invalid_index;
+                    return;
+                }
+                if (index > current->size())
+                {
+                    ec = jsonpointer_errc::index_exceeds_array_size;
+                    return;
+                }
+                if (index == current->size())
+                {
+                    current->emplace_back(std::forward<T>(value));
+                    current = std::addressof(current->at(current->size()-1));
+                }
+                else
+                {
+                    auto it2 = current->insert(current->array_range().begin()+index,std::forward<T>(value));
+                    current = std::addressof(*it2);
+                }
+            }
+        }
+        else if (current->is_object())
+        {
+            auto r = current->insert_or_assign(buffer,std::forward<T>(value));
+            current = std::addressof(r.first->value());
+        }
+        else
+        {
+            ec = jsonpointer_errc::expected_object_or_array;
+            return;
+        }
+    }
+
+    // add
+    template<class Json, class StringSource, class T>
+    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
+    add(Json& root, 
+             const StringSource& location_str, 
+             T&& value, 
+             bool create_if_missing,
+             std::error_code& ec)
+    {
+        auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec);
+        if (ec)
+        {
+            return;
+        }
+        add(root, jsonptr, std::forward<T>(value), create_if_missing, ec);
+    }
+
+    template<class Json,class T>
+    void add(Json& root, 
+             const basic_json_pointer<typename Json::char_type>& location, 
+             T&& value, 
+             std::error_code& ec)
+    {
+        add(root, location, std::forward<T>(value), false, ec);
+    }
+
+    template<class Json, class StringSource, class T>
+    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
+    add(Json& root, 
+             const StringSource& location_str, 
+             T&& value, 
+             std::error_code& ec)
+    {
+        add(root, location_str, std::forward<T>(value), false, ec);
+    }
+
+    template<class Json,class T>
+    void add(Json& root, 
+             const basic_json_pointer<typename Json::char_type>& location, 
+             T&& value,
+             bool create_if_missing = false)
+    {
+        std::error_code ec;
+        add(root, location, std::forward<T>(value), create_if_missing, ec);
+        if (ec)
+        {
+            JSONCONS_THROW(jsonpointer_error(ec));
+        }
+    }
+
+    template<class Json, class StringSource, class T>
+    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
+    add(Json& root, 
+             const StringSource& location_str, 
+             T&& value,
+             bool create_if_missing = false)
+    {
+        std::error_code ec;
+        add(root, location_str, std::forward<T>(value), create_if_missing, ec);
+        if (ec)
+        {
+            JSONCONS_THROW(jsonpointer_error(ec));
+        }
+    }
+
+    // add_if_absent
+
+    template<class Json, class T>
+    void add_if_absent(Json& root, 
+                       const basic_json_pointer<typename Json::char_type>& location, 
+                       T&& value, 
+                       bool create_if_missing,
+                       std::error_code& ec)
+    {
+        Json* current = std::addressof(root);
+
+        std::basic_string<typename Json::char_type> buffer;
+        auto it = location.begin();
+        auto end = location.end();
+
+        while (it != end)
+        {
+            buffer = *it;
+            ++it;
+            if (it != end)
+            {
+                current = jsoncons::jsonpointer::detail::resolve(current, buffer, create_if_missing, ec);
+                if (ec)
+                    return;
+            }
+        }
+        if (current->is_array())
+        {
+            if (buffer.size() == 1 && buffer[0] == '-')
+            {
+                current->emplace_back(std::forward<T>(value));
+                current = std::addressof(current->at(current->size()-1));
+            }
+            else
+            {
+                std::size_t index{0};
+                auto result = jsoncons::detail::to_integer_decimal(buffer.data(), buffer.length(), index);
+                if (!result)
+                {
+                    ec = jsonpointer_errc::invalid_index;
+                    return;
+                }
+                if (index > current->size())
+                {
+                    ec = jsonpointer_errc::index_exceeds_array_size;
+                    return;
+                }
+                if (index == current->size())
+                {
+                    current->emplace_back(std::forward<T>(value));
+                    current = std::addressof(current->at(current->size()-1));
+                }
+                else
+                {
+                    auto it2 = current->insert(current->array_range().begin()+index,std::forward<T>(value));
+                    current = std::addressof(*it2);
+                }
+            }
+        }
+        else if (current->is_object())
+        {
+            if (current->contains(buffer))
+            {
+                ec = jsonpointer_errc::key_already_exists;
+                return;
+            }
+            else
+            {
+                auto r = current->try_emplace(buffer,std::forward<T>(value));
+                current = std::addressof(r.first->value());
+            }
+        }
+        else
+        {
+            ec = jsonpointer_errc::expected_object_or_array;
+            return;
+        }
+    }
+
+    template<class Json, class StringSource, class T>
+    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
+    add_if_absent(Json& root, 
+                       const StringSource& location_str, 
+                       T&& value, 
+                       bool create_if_missing,
+                       std::error_code& ec)
+    {
+        auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec);
+        if (ec)
+        {
+            return;
+        }
+        add_if_absent(root, jsonptr, std::forward<T>(value), create_if_missing, ec);
+    }
+
+    template<class Json, class StringSource, class T>
+    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
+    add_if_absent(Json& root, 
+                const StringSource& location, 
+                T&& value, 
+                std::error_code& ec)
+    {
+        add_if_absent(root, location, std::forward<T>(value), false, ec);
+    }
+
+    template<class Json, class StringSource, class T>
+    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
+    add_if_absent(Json& root, 
+                const StringSource& location_str, 
+                T&& value,
+                bool create_if_missing = false)
+    {
+        std::error_code ec;
+        add_if_absent(root, location_str, std::forward<T>(value), create_if_missing, ec);
+        if (ec)
+        {
+            JSONCONS_THROW(jsonpointer_error(ec));
+        }
+    }
+
+    template<class Json, class T>
+    void add_if_absent(Json& root, 
+                       const basic_json_pointer<typename Json::char_type>& location, 
+                       T&& value, 
+                       std::error_code& ec)
+    {
+        add_if_absent(root, location, std::forward<T>(value), false, ec);
+    }
+
+    template<class Json, class T>
+    void add_if_absent(Json& root, 
+                const basic_json_pointer<typename Json::char_type>& location, 
+                T&& value,
+                bool create_if_missing = false)
+    {
+        std::error_code ec;
+        add_if_absent(root, location, std::forward<T>(value), create_if_missing, ec);
+        if (ec)
+        {
+            JSONCONS_THROW(jsonpointer_error(ec));
+        }
+    }
+
+    // remove
+
+    template<class Json>
+    void remove(Json& root, const basic_json_pointer<typename Json::char_type>& location, std::error_code& ec)
+    {
+        Json* current = std::addressof(root);
+
+        std::basic_string<typename Json::char_type> buffer;
+        auto it = location.begin();
+        auto end = location.end();
+
+        while (it != end)
+        {
+            buffer = *it;
+            ++it;
+            if (it != end)
+            {
+                current = jsoncons::jsonpointer::detail::resolve(current, buffer, false, ec);
+                if (ec)
+                    return;
+            }
+        }
+        if (current->is_array())
+        {
+            if (buffer.size() == 1 && buffer[0] == '-')
+            {
+                ec = jsonpointer_errc::index_exceeds_array_size;
+                return;
+            }
+            else
+            {
+                std::size_t index{0};
+                auto result = jsoncons::detail::to_integer_decimal(buffer.data(), buffer.length(), index);
+                if (!result)
+                {
+                    ec = jsonpointer_errc::invalid_index;
+                    return;
+                }
+                if (index >= current->size())
+                {
+                    ec = jsonpointer_errc::index_exceeds_array_size;
+                    return;
+                }
+                current->erase(current->array_range().begin()+index);
+            }
+        }
+        else if (current->is_object())
+        {
+            if (!current->contains(buffer))
+            {
+                ec = jsonpointer_errc::key_not_found;
+                return;
+            }
+            else
+            {
+                current->erase(buffer);
+            }
+        }
+        else
+        {
+            ec = jsonpointer_errc::expected_object_or_array;
+            return;
+        }
+    }
+
+    template<class Json, class StringSource>
+    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
+    remove(Json& root, const StringSource& location_str, std::error_code& ec)
+    {
+        auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec);
+        if (ec)
+        {
+            return;
+        }
+        remove(root, jsonptr, ec);
+    }
+
+    template<class Json, class StringSource>
+    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
+    remove(Json& root, const StringSource& location_str)
+    {
+        std::error_code ec;
+        remove(root, location_str, ec);
+        if (ec)
+        {
+            JSONCONS_THROW(jsonpointer_error(ec));
+        }
+    }
+
+    template<class Json>
+    void remove(Json& root, const basic_json_pointer<typename Json::char_type>& location)
+    {
+        std::error_code ec;
+        remove(root, location, ec);
+        if (ec)
+        {
+            JSONCONS_THROW(jsonpointer_error(ec));
+        }
+    }
+
+    // replace
+
+    template<class Json, class T>
+    void replace(Json& root, 
+                 const basic_json_pointer<typename Json::char_type>& location, 
+                 T&& value, 
+                 bool create_if_missing,
+                 std::error_code& ec)
+    {
+        Json* current = std::addressof(root);
+
+        std::basic_string<typename Json::char_type> buffer;
+        auto it = location.begin();
+        auto end = location.end();
+
+        while (it != end)
+        {
+            buffer = *it;
+            ++it;
+            if (it != end)
+            {
+                current = jsoncons::jsonpointer::detail::resolve(current, buffer, create_if_missing, ec);
+                if (ec)
+                    return;
+            }
+        }
+        if (current->is_array())
+        {
+            if (buffer.size() == 1 && buffer[0] == '-')
+            {
+                ec = jsonpointer_errc::index_exceeds_array_size;
+                return;
+            }
+            else
+            {
+                std::size_t index{};
+                auto result = jsoncons::detail::to_integer_decimal(buffer.data(), buffer.length(), index);
+                if (!result)
+                {
+                    ec = jsonpointer_errc::invalid_index;
+                    return;
+                }
+                if (index >= current->size())
+                {
+                    ec = jsonpointer_errc::index_exceeds_array_size;
+                    return;
+                }
+                current->at(index) = std::forward<T>(value);
+            }
+        }
+        else if (current->is_object())
+        {
+            if (!current->contains(buffer))
+            {
+                if (create_if_missing)
+                {
+                    current->try_emplace(buffer,std::forward<T>(value));
+                }
+                else
+                {
+                    ec = jsonpointer_errc::key_not_found;
+                    return;
+                }
+            }
+            else
+            {
+                auto r = current->insert_or_assign(buffer,std::forward<T>(value));
+                current = std::addressof(r.first->value());
+            }
+        }
+        else
+        {
+            ec = jsonpointer_errc::expected_object_or_array;
+            return;
+        }
+    }
+
+    template<class Json, class StringSource, class T>
+    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
+    replace(Json& root, 
+                 const StringSource& location_str, 
+                 T&& value, 
+                 bool create_if_missing,
+                 std::error_code& ec)
+    {
+        auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec);
+        if (ec)
+        {
+            return;
+        }
+        replace(root, jsonptr, std::forward<T>(value), create_if_missing, ec);
+    }
+
+    template<class Json, class StringSource, class T>
+    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
+    replace(Json& root, 
+                 const StringSource& location_str, 
+                 T&& value, 
+                 std::error_code& ec)
+    {
+        replace(root, location_str, std::forward<T>(value), false, ec);
+    }
+
+    template<class Json, class StringSource, class T>
+    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
+    replace(Json& root, 
+                 const StringSource& location_str, 
+                 T&& value, 
+                 bool create_if_missing = false)
+    {
+        std::error_code ec;
+        replace(root, location_str, std::forward<T>(value), create_if_missing, ec);
+        if (ec)
+        {
+            JSONCONS_THROW(jsonpointer_error(ec));
+        }
+    }
+
+    template<class Json, class T>
+    void replace(Json& root, 
+                 const basic_json_pointer<typename Json::char_type>& location, 
+                 T&& value, 
+                 std::error_code& ec)
+    {
+        replace(root, location, std::forward<T>(value), false, ec);
+    }
+
+    template<class Json, class T>
+    void replace(Json& root, 
+                 const basic_json_pointer<typename Json::char_type>& location, 
+                 T&& value, 
+                 bool create_if_missing = false)
+    {
+        std::error_code ec;
+        replace(root, location, std::forward<T>(value), create_if_missing, ec);
+        if (ec)
+        {
+            JSONCONS_THROW(jsonpointer_error(ec));
+        }
+    }
+
+    template <class String,class Result>
+    typename std::enable_if<std::is_convertible<typename String::value_type,typename Result::value_type>::value>::type
+    escape(const String& s, Result& result)
+    {
+        for (auto c : s)
+        {
+            if (c == '~')
+            {
+                result.push_back('~');
+                result.push_back('0');
+            }
+            else if (c == '/')
+            {
+                result.push_back('~');
+                result.push_back('1');
+            }
+            else
+            {
+                result.push_back(c);
+            }
+        }
+    }
+
+    template <class CharT>
+    std::basic_string<CharT> escape(const jsoncons::basic_string_view<CharT>& s)
+    {
+        std::basic_string<CharT> result;
+
+        for (auto c : s)
+        {
+            if (c == '~')
+            {
+                result.push_back('~');
+                result.push_back('0');
+            }
+            else if (c == '/')
+            {
+                result.push_back('~');
+                result.push_back('1');
+            }
+            else
+            {
+                result.push_back(c);
+            }
+        }
+        return result;
+    }
+
+    // flatten
+
+    template<class Json>
+    void flatten_(const std::basic_string<typename Json::char_type>& parent_key,
+                  const Json& parent_value,
+                  Json& result)
+    {
+        using char_type = typename Json::char_type;
+        using string_type = std::basic_string<char_type>;
+
+        switch (parent_value.type())
+        {
+            case json_type::array_value:
+            {
+                if (parent_value.empty())
+                {
+                    // Flatten empty array to null
+                    //result.try_emplace(parent_key, null_type{});
+                    //result[parent_key] = parent_value;
+                    result.try_emplace(parent_key, parent_value);
+                }
+                else
+                {
+                    for (std::size_t i = 0; i < parent_value.size(); ++i)
+                    {
+                        string_type key(parent_key);
+                        key.push_back('/');
+                        jsoncons::detail::from_integer(i,key);
+                        flatten_(key, parent_value.at(i), result);
+                    }
+                }
+                break;
+            }
+
+            case json_type::object_value:
+            {
+                if (parent_value.empty())
+                {
+                    // Flatten empty object to null
+                    //result.try_emplace(parent_key, null_type{});
+                    //result[parent_key] = parent_value;
+                    result.try_emplace(parent_key, parent_value);
+                }
+                else
+                {
+                    for (const auto& item : parent_value.object_range())
+                    {
+                        string_type key(parent_key);
+                        key.push_back('/');
+                        escape(jsoncons::basic_string_view<char_type>(item.key().data(),item.key().size()), key);
+                        flatten_(key, item.value(), result);
+                    }
+                }
+                break;
+            }
+
+            default:
+            {
+                // add primitive parent_value with its reference string
+                //result[parent_key] = parent_value;
+                result.try_emplace(parent_key, parent_value);
+                break;
+            }
+        }
+    }
+
+    template<class Json>
+    Json flatten(const Json& value)
+    {
+        Json result;
+        std::basic_string<typename Json::char_type> parent_key;
+        flatten_(parent_key, value, result);
+        return result;
+    }
+
+
+    // unflatten
+
+    enum class unflatten_options {none,assume_object = 1
+    #if !defined(JSONCONS_NO_DEPRECATED)
+,object = assume_object
+#endif
+};
+
+    template<class Json>
+    Json safe_unflatten (Json& value)
+    {
+        if (!value.is_object() || value.empty())
+        {
+            return value;
+        }
+        bool safe = true;
+        std::size_t index = 0;
+        for (const auto& item : value.object_range())
+        {
+            std::size_t n;
+            auto r = jsoncons::detail::to_integer_decimal(item.key().data(),item.key().size(), n);
+            if (!r || (index++ != n))
+            {
+                safe = false;
+                break;
+            }
+        }
+
+        if (safe)
+        {
+            Json j(json_array_arg);
+            j.reserve(value.size());
+            for (auto& item : value.object_range())
+            {
+                j.emplace_back(std::move(item.value()));
+            }
+            Json a(json_array_arg);
+            for (auto& item : j.array_range())
+            {
+                a.emplace_back(safe_unflatten (item));
+            }
+            return a;
+        }
+        else
+        {
+            Json o(json_object_arg);
+            for (auto& item : value.object_range())
+            {
+                o.try_emplace(item.key(), safe_unflatten (item.value()));
+            }
+            return o;
+        }
+    }
+
+    template<class Json>
+    jsoncons::optional<Json> try_unflatten_array(const Json& value)
+    {
+        using char_type = typename Json::char_type;
+
+        if (JSONCONS_UNLIKELY(!value.is_object()))
+        {
+            JSONCONS_THROW(jsonpointer_error(jsonpointer_errc::argument_to_unflatten_invalid));
+        }
+        Json result;
+
+        for (const auto& item: value.object_range())
+        {
+            Json* part = &result;
+            basic_json_pointer<char_type> ptr(item.key());
+            std::size_t index = 0;
+            for (auto it = ptr.begin(); it != ptr.end(); )
+            {
+                auto s = *it;
+                size_t n{0};
+                auto r = jsoncons::detail::to_integer_decimal(s.data(), s.size(), n);
+                if (r.ec == jsoncons::detail::to_integer_errc() && (index++ == n))
+                {
+                    if (!part->is_array())
+                    {
+                        *part = Json(json_array_arg);
+                    }
+                    if (++it != ptr.end())
+                    {
+                        if (n+1 > part->size())
+                        {
+                            Json& ref = part->emplace_back();
+                            part = std::addressof(ref);
+                        }
+                        else
+                        {
+                            part = &part->at(n);
+                        }
+                    }
+                    else
+                    {
+                        Json& ref = part->emplace_back(item.value());
+                        part = std::addressof(ref);
+                    }
+                }
+                else if (part->is_object())
+                {
+                    if (++it != ptr.end())
+                    {
+                        auto res = part->try_emplace(s,Json());
+                        part = &(res.first->value());
+                    }
+                    else
+                    {
+                        auto res = part->try_emplace(s, item.value());
+                        part = &(res.first->value());
+                    }
+                }
+                else 
+                {
+                    return jsoncons::optional<Json>();
+                }
+            }
+        }
+
+        return result;
+    }
+
+    template<class Json>
+    Json unflatten_to_object(const Json& value, unflatten_options options = unflatten_options::none)
+    {
+        using char_type = typename Json::char_type;
+
+        if (JSONCONS_UNLIKELY(!value.is_object()))
+        {
+            JSONCONS_THROW(jsonpointer_error(jsonpointer_errc::argument_to_unflatten_invalid));
+        }
+        Json result;
+
+        for (const auto& item: value.object_range())
+        {
+            Json* part = &result;
+            basic_json_pointer<char_type> ptr(item.key());
+            for (auto it = ptr.begin(); it != ptr.end(); )
+            {
+                auto s = *it;
+                if (++it != ptr.end())
+                {
+                    auto res = part->try_emplace(s,Json());
+                    part = &(res.first->value());
+                }
+                else
+                {
+                    auto res = part->try_emplace(s, item.value());
+                    part = &(res.first->value());
+                }
+            }
+        }
+
+        return options == unflatten_options::none ? safe_unflatten (result) : result;
+    }
+
+    template<class Json>
+    Json unflatten(const Json& value, unflatten_options options = unflatten_options::none)
+    {
+        if (options == unflatten_options::none)
+        {
+            jsoncons::optional<Json> j = try_unflatten_array(value);
+            return j ? *j : unflatten_to_object(value,options);
+        }
+        else
+        {
+            return unflatten_to_object(value,options);
+        }
+    }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+
+    template<class Json>
+    JSONCONS_DEPRECATED_MSG("Instead, use add(Json&, const typename Json::string_view_type&, const Json&)")
+    void insert_or_assign(Json& root, const std::basic_string<typename Json::char_type>& location, const Json& value)
+    {
+        add(root, location, value);
+    }
+
+    template<class Json>
+    JSONCONS_DEPRECATED_MSG("Instead, use add(Json&, const typename Json::string_view_type&, const Json&, std::error_code&)")
+    void insert_or_assign(Json& root, const std::basic_string<typename Json::char_type>& location, const Json& value, std::error_code& ec)
+    {
+        add(root, location, value, ec);
+    }
+    template<class Json, class T>
+    void insert(Json& root, 
+                const std::basic_string<typename Json::char_type>& location, 
+                T&& value, 
+                bool create_if_missing,
+                std::error_code& ec)
+    {
+        add_if_absent(root,location,std::forward<T>(value),create_if_missing,ec);
+    }
+
+    template<class Json, class T>
+    void insert(Json& root, 
+                const std::basic_string<typename Json::char_type>& location, 
+                T&& value, 
+                std::error_code& ec)
+    {
+        add_if_absent(root, location, std::forward<T>(value), ec);
+    }
+
+    template<class Json, class T>
+    void insert(Json& root, 
+                const std::basic_string<typename Json::char_type>& location, 
+                T&& value,
+                bool create_if_missing = false)
+    {
+        add_if_absent(root, location, std::forward<T>(value), create_if_missing);
+    }
+#endif
+
+} // namespace jsonpointer
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons_ext/jsonpointer/jsonpointer_error.hpp b/include/jsoncons_ext/jsonpointer/jsonpointer_error.hpp
new file mode 100644
index 0000000..a0cfeff
--- /dev/null
+++ b/include/jsoncons_ext/jsonpointer/jsonpointer_error.hpp
@@ -0,0 +1,119 @@
+// Copyright 2013 Daniel Parker
+// Distributed under the Boost license, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_JSONPOINTER_JSONPOINTER_ERROR_HPP
+#define JSONCONS_JSONPOINTER_JSONPOINTER_ERROR_HPP
+
+#include <jsoncons/json_exception.hpp>
+#include <system_error>
+
+namespace jsoncons { namespace jsonpointer {
+
+class jsonpointer_error : public std::system_error, public virtual json_exception
+{
+public:
+    jsonpointer_error(const std::error_code& ec)
+        : std::system_error(ec)
+    {
+    }
+    jsonpointer_error(const std::error_code& ec, const std::string& what_arg)
+        : std::system_error(ec, what_arg)
+    {
+    }
+    jsonpointer_error(const std::error_code& ec, const char* what_arg)
+        : std::system_error(ec, what_arg)
+    {
+    }
+    jsonpointer_error(const jsonpointer_error& other) = default;
+
+    jsonpointer_error(jsonpointer_error&& other) = default;
+
+    const char* what() const noexcept override
+    {
+        return std::system_error::what();
+    }
+};
+
+enum class jsonpointer_errc 
+{
+    success = 0,
+    expected_slash = 1,
+    index_exceeds_array_size,
+    expected_0_or_1,
+    invalid_index,
+    key_not_found,
+    key_already_exists,
+    expected_object_or_array,
+    end_of_input,
+    unexpected_end_of_input,
+    argument_to_unflatten_invalid,
+    invalid_flattened_key,
+    invalid_uri_escaped_data
+};
+
+class jsonpointer_error_category_impl
+   : public std::error_category
+{
+public:
+    const char* name() const noexcept override
+    {
+        return "jsoncons/jsonpointer";
+    }
+    std::string message(int ev) const override
+    {
+        switch (static_cast<jsonpointer_errc>(ev))
+        {
+            case jsonpointer_errc::expected_slash:
+                return "Expected /";
+            case jsonpointer_errc::index_exceeds_array_size:
+                return "Index exceeds array size";
+            case jsonpointer_errc::expected_0_or_1:
+                return "Expected '0' or '1' after escape character '~'";
+            case jsonpointer_errc::key_not_found:
+                return "Key not found";
+            case jsonpointer_errc::invalid_index:
+                return "Invalid array index";
+            case jsonpointer_errc::key_already_exists:
+                return "Key already exists";
+            case jsonpointer_errc::expected_object_or_array:
+                return "Expected object or array";
+            case jsonpointer_errc::end_of_input:
+                return "Unexpected end of input";
+            case jsonpointer_errc::unexpected_end_of_input:
+                return "Unexpected end of jsonpointer input";
+            case jsonpointer_errc::argument_to_unflatten_invalid:
+                return "Argument to unflatten must be an object";
+            case jsonpointer_errc::invalid_flattened_key:
+                return "Flattened key is invalid";
+            default:
+                return "Unknown jsonpointer error";
+        }
+    }
+};
+
+inline
+const std::error_category& jsonpointer_error_category()
+{
+  static jsonpointer_error_category_impl instance;
+  return instance;
+}
+
+inline 
+std::error_code make_error_code(jsonpointer_errc result)
+{
+    return std::error_code(static_cast<int>(result),jsonpointer_error_category());
+}
+
+}}
+
+namespace std {
+    template<>
+    struct is_error_code_enum<jsoncons::jsonpointer::jsonpointer_errc> : public true_type
+    {
+    };
+}
+
+#endif
-- 
cgit v1.2.3