aboutsummaryrefslogtreecommitdiff
path: root/include/jsoncons_ext/jsonpointer
diff options
context:
space:
mode:
Diffstat (limited to 'include/jsoncons_ext/jsonpointer')
-rw-r--r--include/jsoncons_ext/jsonpointer/jsonpointer.hpp1577
-rw-r--r--include/jsoncons_ext/jsonpointer/jsonpointer_error.hpp119
2 files changed, 1696 insertions, 0 deletions
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