diff options
Diffstat (limited to 'include/jsoncons/decode_traits.hpp')
-rw-r--r-- | include/jsoncons/decode_traits.hpp | 651 |
1 files changed, 651 insertions, 0 deletions
diff --git a/include/jsoncons/decode_traits.hpp b/include/jsoncons/decode_traits.hpp new file mode 100644 index 0000000..2c9ee6e --- /dev/null +++ b/include/jsoncons/decode_traits.hpp @@ -0,0 +1,651 @@ +// 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_DECODE_TRAITS_HPP +#define JSONCONS_DECODE_TRAITS_HPP + +#include <string> +#include <tuple> +#include <array> +#include <memory> +#include <type_traits> // std::enable_if, std::true_type, std::false_type +#include <jsoncons/json_visitor.hpp> +#include <jsoncons/json_decoder.hpp> +#include <jsoncons/json_type_traits.hpp> +#include <jsoncons/staj_cursor.hpp> +#include <jsoncons/conv_error.hpp> +#include <jsoncons/more_type_traits.hpp> + +namespace jsoncons { + + // decode_traits + + template <class T, class CharT, class Enable = void> + struct decode_traits + { + template <class Json,class TempAllocator> + static T decode(basic_staj_cursor<CharT>& cursor, + json_decoder<Json,TempAllocator>& decoder, + std::error_code& ec) + { + decoder.reset(); + cursor.read_to(decoder, ec); + if (ec) + { + JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); + } + else if (!decoder.is_valid()) + { + JSONCONS_THROW(ser_error(conv_errc::conversion_failed, cursor.context().line(), cursor.context().column())); + } + return decoder.get_result().template as<T>(); + } + }; + + // specializations + + // primitive + + template <class T, class CharT> + struct decode_traits<T,CharT, + typename std::enable_if<type_traits::is_primitive<T>::value + >::type> + { + template <class Json,class TempAllocator> + static T decode(basic_staj_cursor<CharT>& cursor, + json_decoder<Json,TempAllocator>&, + std::error_code& ec) + { + T v = cursor.current().template get<T>(ec); + return v; + } + }; + + // string + + template <class T, class CharT> + struct decode_traits<T,CharT, + typename std::enable_if<type_traits::is_basic_string<T>::value && + std::is_same<typename T::value_type,CharT>::value + >::type> + { + template <class Json,class TempAllocator> + static T decode(basic_staj_cursor<CharT>& cursor, + json_decoder<Json,TempAllocator>&, + std::error_code& ec) + { + T v = cursor.current().template get<T>(ec); + return v; + } + }; + + template <class T, class CharT> + struct decode_traits<T,CharT, + typename std::enable_if<type_traits::is_basic_string<T>::value && + !std::is_same<typename T::value_type,CharT>::value + >::type> + { + template <class Json,class TempAllocator> + static T decode(basic_staj_cursor<CharT>& cursor, + json_decoder<Json,TempAllocator>&, + std::error_code& ec) + { + auto val = cursor.current().template get<std::basic_string<CharT>>(ec); + T s; + if (!ec) + { + unicode_traits::convert(val.data(), val.size(), s); + } + return s; + } + }; + + // std::pair + + template <class T1, class T2, class CharT> + struct decode_traits<std::pair<T1, T2>, CharT> + { + template <class Json, class TempAllocator> + static std::pair<T1, T2> decode(basic_staj_cursor<CharT>& cursor, + json_decoder<Json, TempAllocator>& decoder, + std::error_code& ec) + { + using value_type = std::pair<T1, T2>; + cursor.array_expected(ec); + if (ec) + { + return value_type{}; + } + if (cursor.current().event_type() != staj_event_type::begin_array) + { + ec = conv_errc::not_pair; + return value_type(); + } + cursor.next(ec); // skip past array + if (ec) + { + return value_type(); + } + + T1 v1 = decode_traits<T1,CharT>::decode(cursor, decoder, ec); + if (ec) {return value_type();} + cursor.next(ec); + if (ec) {return value_type();} + T2 v2 = decode_traits<T2, CharT>::decode(cursor, decoder, ec); + if (ec) {return value_type();} + cursor.next(ec); + + if (cursor.current().event_type() != staj_event_type::end_array) + { + ec = conv_errc::not_pair; + return value_type(); + } + return std::make_pair(v1, v2); + } + }; + + // vector like + template <class T, class CharT> + struct decode_traits<T,CharT, + typename std::enable_if<!is_json_type_traits_declared<T>::value && + type_traits::is_list_like<T>::value && + type_traits::is_back_insertable<T>::value && + !type_traits::is_typed_array<T>::value + >::type> + { + using value_type = typename T::value_type; + + template <class Json,class TempAllocator> + static T decode(basic_staj_cursor<CharT>& cursor, + json_decoder<Json,TempAllocator>& decoder, + std::error_code& ec) + { + T v; + + cursor.array_expected(ec); + if (ec) + { + return T{}; + } + if (cursor.current().event_type() != staj_event_type::begin_array) + { + ec = conv_errc::not_vector; + return v; + } + cursor.next(ec); + while (cursor.current().event_type() != staj_event_type::end_array && !ec) + { + v.push_back(decode_traits<value_type,CharT>::decode(cursor, decoder, ec)); + if (ec) {return T{};} + cursor.next(ec); + } + return v; + } + }; + + template <class T> + struct typed_array_visitor : public default_json_visitor + { + T& v_; + int level_; + public: + using value_type = typename T::value_type; + + typed_array_visitor(T& v) + : default_json_visitor(false,conv_errc::not_vector), v_(v), level_(0) + { + } + private: + bool visit_begin_array(semantic_tag, + const ser_context&, + std::error_code& ec) override + { + if (++level_ != 1) + { + ec = conv_errc::not_vector; + return false; + } + return true; + } + + bool visit_begin_array(std::size_t size, + semantic_tag, + const ser_context&, + std::error_code& ec) override + { + if (++level_ != 1) + { + ec = conv_errc::not_vector; + return false; + } + if (size > 0) + { + reserve_storage(typename std::integral_constant<bool, type_traits::has_reserve<T>::value>::type(), v_, size); + } + return true; + } + + bool visit_end_array(const ser_context&, + std::error_code& ec) override + { + if (level_ != 1) + { + ec = conv_errc::not_vector; + return false; + } + return false; + } + + bool visit_uint64(uint64_t value, + semantic_tag, + const ser_context&, + std::error_code&) override + { + v_.push_back(static_cast<value_type>(value)); + return true; + } + + bool visit_int64(int64_t value, + semantic_tag, + const ser_context&, + std::error_code&) override + { + v_.push_back(static_cast<value_type>(value)); + return true; + } + + bool visit_half(uint16_t value, + semantic_tag, + const ser_context&, + std::error_code&) override + { + return visit_half_(typename std::integral_constant<bool, std::is_integral<value_type>::value>::type(), value); + } + + bool visit_half_(std::true_type, uint16_t value) + { + v_.push_back(static_cast<value_type>(value)); + return true; + } + + bool visit_half_(std::false_type, uint16_t value) + { + v_.push_back(static_cast<value_type>(binary::decode_half(value))); + return true; + } + + bool visit_double(double value, + semantic_tag, + const ser_context&, + std::error_code&) override + { + v_.push_back(static_cast<value_type>(value)); + return true; + } + + bool visit_typed_array(const jsoncons::span<const value_type>& data, + semantic_tag, + const ser_context&, + std::error_code&) override + { + v_ = std::vector<value_type>(data.begin(),data.end()); + return false; + } + + static + void reserve_storage(std::true_type, T& v, std::size_t new_cap) + { + v.reserve(new_cap); + } + + static + void reserve_storage(std::false_type, T&, std::size_t) + { + } + }; + + template <class T, class CharT> + struct decode_traits<T,CharT, + typename std::enable_if<!is_json_type_traits_declared<T>::value && + type_traits::is_list_like<T>::value && + type_traits::is_back_insertable_byte_container<T>::value && + type_traits::is_typed_array<T>::value + >::type> + { + using value_type = typename T::value_type; + + template <class Json,class TempAllocator> + static T decode(basic_staj_cursor<CharT>& cursor, + json_decoder<Json,TempAllocator>&, + std::error_code& ec) + { + cursor.array_expected(ec); + if (ec) + { + return T{}; + } + switch (cursor.current().event_type()) + { + case staj_event_type::byte_string_value: + { + auto bytes = cursor.current().template get<byte_string_view>(ec); + if (!ec) + { + T v; + if (cursor.current().size() > 0) + { + reserve_storage(typename std::integral_constant<bool, type_traits::has_reserve<T>::value>::type(), v, cursor.current().size()); + } + for (auto ch : bytes) + { + v.push_back(static_cast<value_type>(ch)); + } + cursor.next(ec); + return v; + } + else + { + return T{}; + } + } + case staj_event_type::begin_array: + { + T v; + if (cursor.current().size() > 0) + { + reserve_storage(typename std::integral_constant<bool, type_traits::has_reserve<T>::value>::type(), v, cursor.current().size()); + } + typed_array_visitor<T> visitor(v); + cursor.read_to(visitor, ec); + return v; + } + default: + { + ec = conv_errc::not_vector; + return T{}; + } + } + } + + static void reserve_storage(std::true_type, T& v, std::size_t new_cap) + { + v.reserve(new_cap); + } + + static void reserve_storage(std::false_type, T&, std::size_t) + { + } + }; + + template <class T, class CharT> + struct decode_traits<T,CharT, + typename std::enable_if<!is_json_type_traits_declared<T>::value && + type_traits::is_list_like<T>::value && + type_traits::is_back_insertable<T>::value && + !type_traits::is_back_insertable_byte_container<T>::value && + type_traits::is_typed_array<T>::value + >::type> + { + using value_type = typename T::value_type; + + template <class Json,class TempAllocator> + static T decode(basic_staj_cursor<CharT>& cursor, + json_decoder<Json,TempAllocator>&, + std::error_code& ec) + { + cursor.array_expected(ec); + if (ec) + { + return T{}; + } + switch (cursor.current().event_type()) + { + case staj_event_type::begin_array: + { + T v; + if (cursor.current().size() > 0) + { + reserve_storage(typename std::integral_constant<bool, type_traits::has_reserve<T>::value>::type(), v, cursor.current().size()); + } + typed_array_visitor<T> visitor(v); + cursor.read_to(visitor, ec); + return v; + } + default: + { + ec = conv_errc::not_vector; + return T{}; + } + } + } + + static void reserve_storage(std::true_type, T& v, std::size_t new_cap) + { + v.reserve(new_cap); + } + + static void reserve_storage(std::false_type, T&, std::size_t) + { + } + }; + + // set like + template <class T, class CharT> + struct decode_traits<T,CharT, + typename std::enable_if<!is_json_type_traits_declared<T>::value && + type_traits::is_list_like<T>::value && + !type_traits::is_back_insertable<T>::value && + type_traits::is_insertable<T>::value + >::type> + { + using value_type = typename T::value_type; + + template <class Json,class TempAllocator> + static T decode(basic_staj_cursor<CharT>& cursor, + json_decoder<Json,TempAllocator>& decoder, + std::error_code& ec) + { + T v; + + cursor.array_expected(ec); + if (ec) + { + return T{}; + } + if (cursor.current().event_type() != staj_event_type::begin_array) + { + ec = conv_errc::not_vector; + return v; + } + if (cursor.current().size() > 0) + { + reserve_storage(typename std::integral_constant<bool, type_traits::has_reserve<T>::value>::type(), v, cursor.current().size()); + } + cursor.next(ec); + while (cursor.current().event_type() != staj_event_type::end_array && !ec) + { + v.insert(decode_traits<value_type,CharT>::decode(cursor, decoder, ec)); + if (ec) {return T{};} + cursor.next(ec); + if (ec) {return T{};} + } + return v; + } + + static void reserve_storage(std::true_type, T& v, std::size_t new_cap) + { + v.reserve(new_cap); + } + + static void reserve_storage(std::false_type, T&, std::size_t) + { + } + }; + + // std::array + + template <class T, class CharT, std::size_t N> + struct decode_traits<std::array<T,N>,CharT> + { + using value_type = typename std::array<T,N>::value_type; + + template <class Json,class TempAllocator> + static std::array<T, N> decode(basic_staj_cursor<CharT>& cursor, + json_decoder<Json,TempAllocator>& decoder, + std::error_code& ec) + { + std::array<T,N> v; + cursor.array_expected(ec); + if (ec) + { + v.fill(T()); + return v; + } + v.fill(T{}); + if (cursor.current().event_type() != staj_event_type::begin_array) + { + ec = conv_errc::not_vector; + return v; + } + cursor.next(ec); + for (std::size_t i = 0; i < N && cursor.current().event_type() != staj_event_type::end_array && !ec; ++i) + { + v[i] = decode_traits<value_type,CharT>::decode(cursor, decoder, ec); + if (ec) {return v;} + cursor.next(ec); + if (ec) {return v;} + } + return v; + } + }; + + // map like + + template <class T, class CharT> + struct decode_traits<T,CharT, + typename std::enable_if<!is_json_type_traits_declared<T>::value && + type_traits::is_map_like<T>::value && + type_traits::is_constructible_from_const_pointer_and_size<typename T::key_type>::value + >::type> + { + using mapped_type = typename T::mapped_type; + using value_type = typename T::value_type; + using key_type = typename T::key_type; + + template <class Json,class TempAllocator> + static T decode(basic_staj_cursor<CharT>& cursor, + json_decoder<Json,TempAllocator>& decoder, + std::error_code& ec) + { + T val; + if (cursor.current().event_type() != staj_event_type::begin_object) + { + ec = conv_errc::not_map; + return val; + } + if (cursor.current().size() > 0) + { + reserve_storage(typename std::integral_constant<bool, type_traits::has_reserve<T>::value>::type(), val, cursor.current().size()); + } + cursor.next(ec); + + while (cursor.current().event_type() != staj_event_type::end_object && !ec) + { + if (cursor.current().event_type() != staj_event_type::key) + { + ec = json_errc::expected_key; + return val; + } + auto key = cursor.current().template get<key_type>(ec); + if (ec) return val; + cursor.next(ec); + if (ec) return val; + val.emplace(std::move(key),decode_traits<mapped_type,CharT>::decode(cursor, decoder, ec)); + if (ec) {return val;} + cursor.next(ec); + if (ec) {return val;} + } + return val; + } + + static void reserve_storage(std::true_type, T& v, std::size_t new_cap) + { + v.reserve(new_cap); + } + + static void reserve_storage(std::false_type, T&, std::size_t) + { + } + }; + + template <class T, class CharT> + struct decode_traits<T,CharT, + typename std::enable_if<!is_json_type_traits_declared<T>::value && + type_traits::is_map_like<T>::value && + std::is_integral<typename T::key_type>::value + >::type> + { + using mapped_type = typename T::mapped_type; + using value_type = typename T::value_type; + using key_type = typename T::key_type; + + template <class Json,class TempAllocator> + static T decode(basic_staj_cursor<CharT>& cursor, + json_decoder<Json,TempAllocator>& decoder, + std::error_code& ec) + { + T val; + if (cursor.current().event_type() != staj_event_type::begin_object) + { + ec = conv_errc::not_map; + return val; + } + if (cursor.current().size() > 0) + { + reserve_storage(typename std::integral_constant<bool, type_traits::has_reserve<T>::value>::type(), val, cursor.current().size()); + } + cursor.next(ec); + + while (cursor.current().event_type() != staj_event_type::end_object && !ec) + { + if (cursor.current().event_type() != staj_event_type::key) + { + ec = json_errc::expected_key; + return val; + } + auto s = cursor.current().template get<jsoncons::basic_string_view<typename Json::char_type>>(ec); + if (ec) return val; + key_type n{0}; + auto r = jsoncons::detail::to_integer(s.data(), s.size(), n); + if (r.ec != jsoncons::detail::to_integer_errc()) + { + ec = json_errc::invalid_number; + return val; + } + cursor.next(ec); + if (ec) return val; + val.emplace(n, decode_traits<mapped_type,CharT>::decode(cursor, decoder, ec)); + if (ec) {return val;} + cursor.next(ec); + if (ec) {return val;} + } + return val; + } + + static void reserve_storage(std::true_type, T& v, std::size_t new_cap) + { + v.reserve(new_cap); + } + + static void reserve_storage(std::false_type, T&, std::size_t) + { + } + }; + +} // jsoncons + +#endif + |