30#include <initializer_list>
49class borrowed_subtree;
51class temporary_source_scope;
54enum class source_lifetime :
unsigned char
79JSONV_PUBLIC void deserialize_entry(deserialization_context& context,
80 const std::type_info& type,
83 void (* destroy)(
void*)
noexcept,
84 source_lifetime lifetime
98template <
typename T,
typename E>
99struct is_expected<
std::expected<T, E>> :
104inline constexpr bool is_expected_v = is_expected<T>::value;
107struct expected_value_or_self
112template <
typename T,
typename E>
113struct expected_value_or_self<
std::expected<T, E>>
119using expected_value_or_self_t =
typename expected_value_or_self<T>::type;
133 public std::runtime_error
191 std::string _message;
192 std::exception_ptr _cause;
193 std::string _source_name;
196 using problem_list = std::vector<problem>;
227 const
std::
string& source_name() const noexcept;
232 const
std::exception_ptr& nested_ptr() const noexcept;
237 const problem_list& problems() const noexcept {
return _problems; }
240 template <
typename... TArgs>
244 problem_list _problems;
251 using size_type = deserialization_error::problem_list::size_type;
304 on_error failure_mode() const noexcept {
return _failure_mode; };
340 on_error _failure_mode = on_error::fail_immediately;
341 size_type _max_failures = 10U;
355 virtual const std::type_info&
get_type() const noexcept = 0;
422 std::optional<jsonv::version> ver = std::nullopt,
424 const void* userdata =
nullptr,
426 std::string source_name = std::string()
450 bool source_is_temporary() const noexcept {
return _temporary_source_depth != 0U; }
461 const std::string&
source_name() const noexcept {
return _source_name; }
540 template <
typename... TArgs>
542 std::unexpected<ast_node_type>
problem(TArgs&&... args)
545 std::string name = _source_name;
546 auto& recorded = _problems.emplace_back(std::forward<TArgs>(args)...);
547 if (recorded._source_name.empty())
548 recorded._source_name = std::move(name);
550 return std::unexpected(ast_node_type::error);
628 void skip_failed_value(
reader& from) noexcept;
639 void note_value_consumed(const
reader& from) noexcept;
662 template <typename TAstNode>
669 if (
auto matched = expect(from, TAstNode::type()); !matched)
670 return std::unexpected(matched.error());
672 return from.current().as<TAstNode>();
695 template <
typename T>
699 alignas(T) std::byte place[
sizeof(T)];
700 if (
auto result =
deserialize(
typeid(T), from,
static_cast<void*
>(place)); !result)
701 return std::unexpected(result.error());
703 T* ptr = std::launder(
reinterpret_cast<T*
>(place));
704 auto destroy = detail::on_scope_exit([ptr] { std::destroy_at(ptr); });
705 return std::move(*ptr);
721 std::expected<void, ast_node_type>
deserialize(
const std::type_info& type,
reader& from,
void* into);
733 template <
typename T>
766 template <std::
size_t N>
778 template <std::
size_t N>
785 template <
typename TString>
786 requires std::same_as<TString, std::string>
797 template <
typename TString>
798 requires std::same_as<std::remove_const_t<TString>, std::string>
815 void append_to(jsonv::
path& out) const;
825 friend class detail::borrowed_subtree;
826 friend class detail::source_scope;
827 friend class detail::temporary_source_scope;
830 const
std::type_info& type,
833 void (* destroy)(
void*) noexcept,
834 detail::source_lifetime lifetime
841 jsonv::
path take_failure_path(const
reader& from);
845 jsonv::
path _base_path;
846 std::
string _source_name;
848 std::
size_t _temporary_source_depth = 0U;
851 const detail::source_scope* _innermost_source =
nullptr;
860 const
reader* _consumed_failed_value =
nullptr;
932 const std::set<std::string, std::less<>>& keys
958class temporary_source_scope
964 ++_context->_temporary_source_depth;
967 temporary_source_scope(
const temporary_source_scope&) =
delete;
968 temporary_source_scope& operator=(
const temporary_source_scope&) =
delete;
970 ~temporary_source_scope()
noexcept
972 --_context->_temporary_source_depth;
998 _parent(
context._innermost_source)
1000 context._innermost_source =
this;
1010 _showing = shows::value;
1013 source_scope(
const source_scope&) =
delete;
1014 source_scope& operator=(
const source_scope&) =
delete;
1016 ~source_scope()
noexcept
1019 _context->_innermost_source = _parent;
1023 void hide()
noexcept
1026 _showing = shows::nothing;
1043 _bookmark = detail::bookmark(from);
1047 void close(
const reader& from)
1054 _text = std::string_view(_begin,
static_cast<std::size_t
>(end.data() + end.size() - _begin));
1057 _showing = shows::value_and_text;
1063 enum class shows :
unsigned char
1072 void lend_temporary()
const noexcept
1077 ++_context->_temporary_source_depth;
1081 void stop_lending()
const noexcept
1086 --_context->_temporary_source_depth;
1092 const source_scope* _parent;
1093 const reader* _from =
nullptr;
1095 std::optional<parse_index::const_iterator> _bookmark;
1096 const char* _begin =
nullptr;
1097 std::string_view _text;
1098 shows _showing = shows::nothing;
1099 mutable bool _lending =
false;
1103 mutable std::optional<value> _read;
1107 mutable std::optional<std::string> _encoded;
1132 borrowed_subtree(
const borrowed_subtree&) =
delete;
1133 borrowed_subtree& operator=(
const borrowed_subtree&) =
delete;
1135 ~borrowed_subtree()
noexcept;
1138 const value& get()
const noexcept {
return _borrowed ? *_borrowed : _owned; }
1153 int _uncaught_on_entry;
1163template <
typename T,
typename FDeserialize>
1165std::expected<T, ast_node_type>
1168 auto normalise = [](
auto&& result) -> std::expected<T, ast_node_type>
1170 if constexpr (is_expected_v<std::remove_cvref_t<
decltype(result)>>)
1173 return std::forward<decltype(result)>(result).value();
1175 return std::unexpected(result.error());
1179 return std::forward<decltype(result)>(result);
1187 auto normalise_consumed = [&] (
auto&& raw) -> std::expected<T, ast_node_type>
1191 return normalise(std::forward<
decltype(raw)>(raw));
1195 context.note_value_consumed(from);
1200 if constexpr (std::invocable<const FDeserialize&, deserialization_context&, reader&>)
1202 return normalise_consumed(func(
context, from));
1204 else if constexpr (std::invocable<const FDeserialize&, reader&>)
1206 return normalise_consumed(func(from));
1208 else if constexpr (std::invocable<const FDeserialize&, deserialization_context&, const value&>)
1212 borrowed_subtree subtree(
context, from);
1213 auto result = normalise(func(
context, subtree.get()));
1227 context.note_value_consumed(from);
1233 static_assert(std::invocable<const FDeserialize&, const value&>,
1234 "A deserialization function must be callable as (deserialization_context&, reader&), (reader&), "
1235 "(deserialization_context&, const value&) or (const value&)"
1238 borrowed_subtree subtree(
context, from);
1239 auto result = normalise(func(subtree.get()));
1253 context.note_value_consumed(from);
1261template <
typename FDeserialize>
1262struct deserialize_function_result
1264 static auto deduce()
1266 if constexpr (std::invocable<const FDeserialize&, deserialization_context&, reader&>)
1267 return std::type_identity<std::invoke_result_t<const FDeserialize&, deserialization_context&, reader&>>();
1268 else if constexpr (std::invocable<const FDeserialize&, reader&>)
1269 return std::type_identity<std::invoke_result_t<const FDeserialize&, reader&>>();
1270 else if constexpr (std::invocable<const FDeserialize&, deserialization_context&, const value&>)
1271 return std::type_identity<
1272 std::invoke_result_t<const FDeserialize&, deserialization_context&, const value&>>();
1274 return std::type_identity<std::invoke_result_t<const FDeserialize&, const value&>>();
1277 using type = expected_value_or_self_t<std::remove_cvref_t<
typename decltype(deduce())::type>>;
1280template <
typename FDeserialize>
1281using deserialize_function_result_t =
typename deserialize_function_result<FDeserialize>::type;
1284template <
typename T>
1288 alignas(T) std::byte place[
sizeof(T)];
1292 static_cast<void*
>(place),
1293 [](
void* p)
noexcept { std::destroy_at(std::launder(
static_cast<T*
>(p))); },
1297 T* ptr = std::launder(
reinterpret_cast<T*
>(place));
1298 auto destroy = on_scope_exit([ptr] { std::destroy_at(ptr); });
1299 return std::move(*ptr);
1309template <
typename T,
typename TSource>
1313 if constexpr (std::is_same_v<TSource, std::string>)
1315 reader from(std::forward<TSource>(source), parse_opts);
1316 return deserialize_entry<T>(
context, from, source_lifetime::deserialization);
1322 reader from(std::string_view(std::forward<TSource>(source)), parse_opts);
1323 return deserialize_entry<T>(
context, from, source_lifetime::caller);
1328template <
typename T,
typename TSource>
1330T deserialize_text(TSource&& source,
1337 return deserialize_text<T>(std::forward<TSource>(source), parse_opts,
context);
1342template <
typename T>
1343T deserialization_context::deserialize(
const value& from)
1345 reader rdr = reader::from_value(from);
1346 return detail::deserialize_entry<T>(*
this, rdr, detail::source_lifetime::caller);
1350template <
typename T>
1355 return context.deserialize<T>(from);
1359template <
typename T>
1364 return context.deserialize<T>(from);
1368template <
typename T>
1373 return context.deserialize<T>(from);
1377template <
typename T>
1382 return context.deserialize<T>(from);
1404template <
typename T>
1407 const formats& fmts = formats::global(),
1412 return detail::deserialize_entry<T>(
context, from, detail::source_lifetime::caller);
1416template <
typename T>
1420 return deserialize<T>(from, formats::global(), options);
1424template <
typename T>
1427 const formats& fmts = formats::global(),
1432 return detail::deserialize_entry<T>(
context,
1434 from.
owns_source() ? detail::source_lifetime::deserialization
1435 : detail::source_lifetime::caller
1442template <
typename T>
1446 return deserialize<T>(std::move(from), formats::global(), options);
1465template <
typename T>
1469 return detail::deserialize_entry<T>(
context, from, detail::source_lifetime::caller);
1472template <
typename T>
1476 return detail::deserialize_entry<T>(
context,
1478 from.
owns_source() ? detail::source_lifetime::deserialization
1479 : detail::source_lifetime::caller
1504template <
typename T,
typename TSource>
1505 requires std::convertible_to<TSource, std::string_view>
1508 const formats& fmts = formats::global(),
1512 return detail::deserialize_text<T>(std::forward<TSource>(source), parse_options::create_default(), fmts, options);
1516template <
typename T,
typename TSource>
1517 requires std::convertible_to<TSource, std::string_view>
1521 return detail::deserialize_text<T>(std::forward<TSource>(source),
1522 parse_options::create_default(),
1529template <
typename T,
typename TSource>
1530 requires std::convertible_to<TSource, std::string_view>
1534 const formats& fmts = formats::global(),
1538 return detail::deserialize_text<T>(std::forward<TSource>(source), parse_opts, fmts, options);
1543template <
typename T,
typename TSource>
1544 requires std::convertible_to<TSource, std::string_view>
1548 return detail::deserialize_text<T>(std::forward<TSource>(source), parse_opts, formats::global(), options);
1565template <
typename T,
typename TSource>
1566 requires std::convertible_to<TSource, std::string_view>
1570 return detail::deserialize_text<T>(std::forward<TSource>(source), parse_options::create_default(),
context);
1573template <
typename T,
typename TSource>
1574 requires std::convertible_to<TSource, std::string_view>
1578 return detail::deserialize_text<T>(std::forward<TSource>(source), parse_opts,
context);
Utilities for directly dealing with a JSON AST.
std::string_view token_raw() const
The beginning of an kind::object ({).
std::string_view token_raw() const
Get a view of the raw token.
Provides extra information to routines used for deserialization and serialization.
An RAII guard naming one step of the deserialization path while it is alive.
path_scope(deserialization_context &context, const TString &key) noexcept
Name the member key of the object being deserialized, on context.
path_scope(deserialization_context &context, path_element elem)
Name elem on context, keeping a copy of it for as long as this scope lives.
path_scope(deserialization_context &context, std::string_view key) noexcept
Name the member key of the object being deserialized, on context. key is viewed rather than copied.
path_scope(deserialization_context &context, TString &&key)
Name the member key of the object being deserialized, on context, keeping key for as long as this sco...
path_scope(deserialization_context &context, const char(&key)[N]) noexcept
Name the member key of the object being deserialized, on context.
path_scope(deserialization_context &context, std::size_t index) noexcept
Name the element at index of the array being deserialized, on context.
path_scope(deserialization_context &context, const char(&&key)[N])
Name the member key of the object being deserialized, on context, keeping a copy of key for as long a...
Provides extra information to routines used for deserialization, collects the problems they encounter...
jsonv::path problem_path(const reader &from) const
Where to report a problem noticed while from is sitting on the thing that is wrong.
jsonv::path path() const
Get the path currently being deserialized, as named by the live path_scope guards.
std::expected< void, ast_node_type > deserialize(const std::type_info &type, reader &from, void *into)
Attempt to deserialize an object of the given type from from into the memory at into,...
deserialization_error::problem_list problem_list
The problems recorded on a context, in the form a deserialization_error carries them.
std::string_view encoded_source() const
Get the JSON of the object a type described with the serialization builder DSL is being deserialized ...
const problem_list & problems() const &
Get the problems encountered so far. If this list is empty, no problems have occurred.
std::unexpected< ast_node_type > problem(TArgs &&... args)
Note that a problem has been encountered, forwarding args to a deserialization_error::problem.
const deserialize_options & options() const noexcept
Get the options this context is deserializing under.
deserialization_context(jsonv::formats fmt, std::optional< jsonv::version > ver=std::nullopt, jsonv::path p=jsonv::path(), const void *userdata=nullptr, deserialize_options options=deserialize_options(), std::string source_name=std::string())
Create a new instance using the given fmt, ver, p, userdata, options and source_name.
optional< const value & > source_value() const
Get the value a type described with the serialization builder DSL is being deserialized from,...
problem_list && problems() &&
Get the problems encountered so far. If this list is empty, no problems have occurred.
const std::string & source_name() const noexcept
Get the name of the document being deserialized, which every problem recorded here is reported in.
deserialization_context()
Create a new instance using the default formats (formats::global).
std::expected< T, ast_node_type > deserialize(reader &from)
Attempt to deserialize a T from from using the formats associated with this context.
bool recover() const noexcept
May deserialization recover from a failure and keep going?
Description of a single problem with deserialization.
const std::exception_ptr & nested_ptr() const noexcept
If there was an exception that caused this problem, extra details can be found in the nested exceptio...
problem(jsonv::path path, std::exception_ptr cause) noexcept
Create a problem with a message extracted from cause.
const std::string & source_name() const noexcept
The name of the document this problem was encountered in, such as the file it was read from.
problem(jsonv::path path, std::string message) noexcept
Create a problem for the given path, message, and optional cause.
const std::string & message() const noexcept
Human-readable details about the encountered problem.
problem(jsonv::path path, std::string message, std::exception_ptr cause) noexcept
Create a problem for the given path, message, and optional cause.
const jsonv::path & path() const noexcept
The path this problem was encountered at, within the document source_name names.
Exception thrown if there is any problem running deserialize.
deserialization_error(jsonv::path path, std::string message, std::exception_ptr cause) noexcept
Create a new deserialization_error with a single problem from the given path, message,...
deserialization_error(jsonv::path path, std::string message) noexcept
Create a new deserialization_error with a single problem from the given path, message,...
deserialization_error(problem_list problems) noexcept
Create a deserialization_error from the given list of problems.
deserialization_error(jsonv::path path, std::exception_ptr cause) noexcept
Create a new deserialization_error with a single problem at path, whose message is extracted from cau...
Configuration for various deserialization options. This becomes part of the deserialization_context.
deserialize_options() noexcept
Create an instance with the default options.
size_type max_failures() const
The number of problems to collect before giving up.
deserialize_options & failure_mode(on_error mode)
See on_error. The default failure mode is fail_immediately.
deserialize_options & max_failures(size_type limit)
The number of problems to collect before giving up.
on_error
When an error is encountered during deserialization, what should happen?
duplicate_key_action on_duplicate_key() const
See duplicate_key_action. The default action is replace.
duplicate_key_action
When an object key has the same value as a previously-seen key, what should happen?
deserialize_options & on_duplicate_key(duplicate_key_action action)
See duplicate_key_action. The default action is replace.
A deserializer holds the method for converting JSON source into an arbitrary C++ type.
virtual const std::type_info & get_type() const noexcept=0
Get the run-time type this deserializer knows how to deserialize.
Configuration for various parsing options.
Represents an exact path in some JSON structure.
A reader instance reads from some form of JSON source (probably a string) and converts it into a JSON...
bool owns_source() const noexcept
Does this reader own the storage it reads from?
const ast_node & current() const
Get the current AST node this reader is pointing at.
optional< const value & > current_value() const noexcept
Get the in-memory value this reader is positioned on, if it has one to lend.
Represents a single JSON value, which can be any one of a potential kind, each behaving slightly diff...
Copyright (c) 2014-2020 by Travis Gockel.
Copyright (c) 2015-2020 by Travis Gockel.
Copyright (c) 2012-2020 by Travis Gockel.
#define JSONV_NODISCARD
Warn if the caller discards the result of this function.
#define JSONV_PUBLIC
This function or class is part of the public API for JSON Voorhees.
T deserialize(const value &from, const formats &fmts)
Deserialize a C++ value from from using the provided fmts.
@ exception
A duplicate_type_error should be thrown.
@ ignore
The existing deserializer or serializer should be kept, but no exception should be thrown.
@ replace
The new deserializer or serializer should be inserted, and no exception should be thrown.
ast_node_type
Marker type for an encountered token type.
Pulls in an implementation of optional.
typename detail::optional_type< T >::type optional
Represents a value that may or may not be present.
Copyright (c) 2012-2020 by Travis Gockel.
Parsed index of a JSON document.
Definition of the on_scope_exit utility.
Copyright (c) 2012-2020 by Travis Gockel.