87template <
typename TPo
inter>
101 template <
typename T>
104 emplace_subtype<T>(std::move(pred),
false);
111 template <
typename T>
113 value expected_value,
116 std::type_index tidx = std::type_index(
typeid(T));
117 if (!_serialization_actions.emplace(tidx, std::make_tuple(key, expected_value, action)).second)
121 _discriminator_keys.insert(key);
123 match_predicate op = [key = std::move(key), expected_value = std::move(expected_value)]
130 && iter->second == expected_value;
132 emplace_subtype<T>(std::move(op),
true);
141 _check_null_input = on;
147 return _check_null_input;
156 _check_null_output = on;
162 return _check_null_output;
173 if (_check_null_input && (lent ? lent->kind() == jsonv::kind::null
187 context.note_value_consumed(from);
195 std::optional<value> members;
196 std::optional<value> whole;
197 auto subject = [&] (
const subtype& sub) ->
const value&
204 if (sub.keyed && !whole)
207 members.
emplace(detail::peek_members(
context, from, _discriminator_keys));
212 whole.emplace(detail::peek_value(
context, from));
216 const subtype* chosen =
nullptr;
221 std::optional<detail::temporary_source_scope> temporary;
225 for (
const auto& sub : _subtypes)
227 if (sub.predicate(
context, subject(sub)))
238 return chosen->create(
context, from);
242 std::string message =
"No discriminators matched JSON value";
245 const value& unmatched = lent ? *lent
252 return context.problem(
context.problem_path(from), std::move(message));
260 if (_check_null_output && !from)
266 auto action_iter = _serialization_actions.find(std::type_index(
typeid(*from)));
267 if (action_iter == _serialization_actions.end()
271 context.serialize(
typeid(*from),
static_cast<const void*
>(&*from), to);
276 to.
write(keyed_to_json(
context, from, action_iter->second));
281 using serialization_action = std::tuple<std::string, value, keyed_subtype_action>;
287 const TPointer& from,
288 const serialization_action& action_entry
291 value serialized =
context.to_json(
typeid(*from),
static_cast<const void*
>(&*from));
295 return " polymorphic_adapter<" +
demangle(
typeid(TPointer).name()) +
">"
296 "subtype(" +
demangle(
typeid(*from).name()) +
")";
299 const std::string& key = std::get<0>(action_entry);
300 const value& val = std::get<1>(action_entry);
307 throw std::runtime_error(
"Expected keyed subtype to serialize as an object." + errmsg());
308 if (!serialized.
count(key))
309 throw std::runtime_error(
"Expected subtype key not found." + errmsg());
310 if (serialized.
at(key) != val)
311 throw std::runtime_error(
"Expected subtype key is not the expected value." + errmsg());
315 throw std::runtime_error(
"Expected keyed subtype to serialize as an object." + errmsg());
316 if (serialized.
count(key))
317 throw std::runtime_error(
"Subtype key already present when trying to insert." + errmsg());
318 serialized[key] = val;
321 throw std::runtime_error(
"Unknown keyed_subtype_action.");
327 using create_function = std::function<std::expected<TPointer, ast_node_type> (deserialization_context&, reader&)>;
331 match_predicate predicate;
335 create_function create;
338 template <
typename T>
339 void emplace_subtype(match_predicate pred,
bool keyed)
341 _subtypes.push_back(subtype{ std::move(pred),
343 [] (deserialization_context& context, reader& from)
344 -> std::expected<TPointer, ast_node_type>
352 auto deserialized = context.deserialize<T>(from);
354 return std::unexpected(deserialized.error());
356 return TPointer(
new T(*std::move(deserialized)));
360 context.note_value_consumed(from);
369 std::vector<subtype> _subtypes;
370 std::set<std::string, std::less<>> _discriminator_keys;
371 std::map<std::type_index, serialization_action> _serialization_actions;
372 bool _check_null_input =
false;
373 bool _check_null_output =
false;
Copyright (c) 2015-2026 by Travis Gockel.
An adapter for the type T.
Provides extra information to routines used for deserialization and serialization.
Provides extra information to routines used for deserialization, collects the problems they encounter...
Exception thrown if an insertion of a deserializer or serializer into a formats is attempted,...
An adapter which can create polymorphic types.
void add_subtype_keyed(std::string key, value expected_value, keyed_subtype_action action=keyed_subtype_action::none)
Add a subtype which can be transformed into TPointer which will be called if given a JSON value with ...
virtual void serialize(const serialization_context &context, const TPointer &from, writer &to) const override
Write the object from points to where to is, or null for an empty pointer under check_null_output.
bool check_null_output() const
When serializing, should an empty TPointer be written as null?
virtual std::expected< TPointer, ast_node_type > create(deserialization_context &context, reader &from) const override
Create an instance of T by reading from.
bool check_null_input() const
When deserializing a C++ value, should kind::null in JSON automatically become a default-constructed ...
void check_null_output(bool on)
When serializing, should an empty TPointer be written as null?
void check_null_input(bool on)
When deserializing a C++ value, should kind::null in JSON automatically become a default-constructed ...
void add_subtype(match_predicate pred)
Add a subtype which can be transformed into TPointer which will be called if the discriminator pred i...
A reader instance reads from some form of JSON source (probably a string) and converts it into a JSON...
ast_node_type current_type() const
Get the type of the current AST node, which is always current().type().
bool next_token() noexcept
Go to the next token.
optional< const value & > current_value() const noexcept
Get the in-memory value this reader is positioned on, if it has one to lend.
Provides extra information to routines used for serialization: the formats to find other serializers ...
Represents a single JSON value, which can be any one of a potential kind, each behaving slightly diff...
size_type count(const std::string &key) const
Check if the given key exists in this object.
object_iterator end_object()
Get an iterator to the one past the end of this object.
object_iterator find(const std::string &key)
Attempt to locate a key-value pair with the provided key in this object.
value & at(size_type idx)
Get the value in this array at the given idx.
std::pair< object_iterator, bool > emplace(std::string key, value val)
Construct an element from key and val and insert it into this object.
bool is_object() const
Tests if this kind is kind::object.
A writer instance writes a JSON ast_node sequence to some form of sink: an encoder,...
writer & write(const value &source)
Write the whole of source as one value, as encoder::encode does.
writer & null()
Write a scalar value.
Copyright (c) 2014-2020 by Travis Gockel.
Copyright (c) 2015 by Travis Gockel.
#define JSONV_NODISCARD
Warn if the caller discards the result of this function.
JSONV_PUBLIC std::string demangle(std::string_view source)
Convert the input source from a mangled type into a human-friendly version.
keyed_subtype_action
What to do when serializing a keyed subtype of a polymorphic_adapter.
@ check
Ensure the correct key/value pair was inserted by serialization. Throws std::runtime_error if it wasn...
@ none
Don't do any checking or insertion of the expected key/value pair.
@ insert
Insert the correct key/value pair as part of serialization.
JSONV_PUBLIC std::string to_string(const ast_node_type &type)
Get what operator<< writes for type as a std::string.
Copyright (c) 2019-2020 by Travis Gockel.
Pulls in an implementation of optional.
typename detail::optional_type< T >::type optional
Represents a value that may or may not be present.
Conversion between C++ types and JSON values.