JSON Voorhees
Killer JSON for C++
Loading...
Searching...
No Matches
serialization_builder.hpp
Go to the documentation of this file.
1/// \file jsonv/serialization_builder.hpp
2/// DSL for building \c formats.
3///
4/// Copyright (c) 2015-2020 by Travis Gockel. All rights reserved.
5///
6/// This program is free software: you can redistribute it and/or modify it under the terms of the Apache License
7/// as published by the Apache Software Foundation, either version 2 of the License, or (at your option) any later
8/// version.
9///
10/// \author Travis Gockel (travis@gockelhut.com)
11#pragma once
12
13#include <jsonv/config.hpp>
14#include <jsonv/demangle.hpp>
21
22#include <algorithm>
23#include <array>
24#include <concepts>
25#include <cstdint>
26#include <deque>
27#include <expected>
28#include <map>
29#include <memory>
30#include <optional>
31#include <set>
32#include <stdexcept>
33#include <string>
34#include <string_view>
35#include <type_traits>
36#include <vector>
37
38namespace jsonv
39{
40
41/// \page serialization_builder_dsl Serialization Builder DSL
42///
43/// Most applications tend to have a lot of structure types. While it is possible to write a \c deserializer and
44/// \c serializer (or \c adapter) for each type, this can get a little bit tedious. Beyond that, it is very difficult to
45/// look at the contents of adapter code and discover what the JSON might actually look like. The builder DSL is meant
46/// to solve these issues by providing a convenient way to describe conversion operations for your C++ types.
47///
48/// At the end of the day, the goal is to take some C++ structures like this:
49///
50/// \code
51/// struct person
52/// {
53/// std::string first_name;
54/// std::string last_name;
55/// int age;
56/// std::string role;
57/// };
58///
59/// struct company
60/// {
61/// std::string name;
62/// bool certified;
63/// std::vector<person> employees;
64/// std::list<person> candidates;
65/// };
66/// \endcode
67///
68/// ...and easily convert it to an from a JSON representation that looks like this:
69///
70/// \code
71/// {
72/// "name": "Paul's Construction",
73/// "certified": false,
74/// "employees": [
75/// {
76/// "first_name": "Bob",
77/// "last_name": "Builder",
78/// "age": 29
79/// },
80/// {
81/// "first_name": "James",
82/// "last_name": "Johnson",
83/// "age": 38,
84/// "role": "Foreman"
85/// }
86/// ],
87/// "candidates": [
88/// {
89/// "firstname": "Adam",
90/// "lastname": "Ant"
91/// }
92/// ]
93/// }
94/// \endcode
95///
96/// To define a \c formats for this \c person type using the serialization builder DSL, you would say:
97///
98/// \code
99/// jsonv::formats fmts =
100/// jsonv::formats_builder()
101/// .type<person>()
102/// .member("first_name", &person::first_name)
103/// .alias("firstname")
104/// .member("last_name", &person::last_name)
105/// .alias("lastname")
106/// .member("age", &person::age)
107/// .until({ 6,1 })
108/// .default_value(21)
109/// .default_on_null()
110/// .check([] (int value) { if (value < 0) throw std::logic_error("Age must be positive."); })
111/// .member("role", &person::role)
112/// .since({ 2,0 })
113/// .default_value("Builder")
114/// .type<company>()
115/// .member("name", &company::name)
116/// .member("certified", &company::certified)
117/// .member("employees", &company::employees)
118/// .member("candidates", &company::candidates)
119/// .register_containers<person, std::vector, std::list>()
120/// .check_references(jsonv::formats::defaults())
121/// ;
122/// \endcode
123///
124/// \section serialization_builder_dsl_deserialization Deserialization
125///
126/// A type described with the DSL is deserialized by walking its JSON object's keys in the order the document wrote
127/// them, handing each one to the member which claims it -- by the name the member was declared with, or by one of
128/// its \ref serialization_builder_dsl_ref_member_level_alias "aliases". Members are deserialized in
129/// document order rather than declaration order, so that is also the order a member's \c check and setter run
130/// in and the order problems are reported in. Once the walk reaches the end of the object, each member no key
131/// claimed is given its \ref serialization_builder_dsl_ref_member_level_default_value "default value" or, if it has
132/// none, reported as missing; that pass, being over the members rather than the keys, goes in declaration order. A
133/// member whose key held a \c null under \ref serialization_builder_dsl_ref_member_level_default_on_null
134/// "default_on_null" is given its default in the same pass, since that is what the \c null stands for.
135///
136/// A key which no member claims is skipped rather than collected: its value is stepped over unread, in a single step
137/// however large it is, and never built into a \c value nobody asked for. Its name is all that is kept, and only when
138/// something needs it. An \ref serialization_builder_dsl_ref_type_level_on_unknown_members "on_unknown_members"
139/// handler is handed the names of all of them once the walk is done. And a \c deserialize_options::on_duplicate_key of
140/// \c duplicate_key_action::exception remembers every key, claimed or not, so that the object is refused if one comes
141/// round again.
142///
143/// No hook is handed the JSON being read as an argument, but every one which takes a \c deserialization_context can ask
144/// it for the object. \c deserialization_context::source_value has it as a \c value to read members out of: for
145/// \c pre_deserialize, before the walk, and for \c on_unknown_members, every \c default_value and
146/// \c post_deserialize after it. Once the walk has reached the object's \c },
147/// \c deserialization_context::encoded_source quotes the whole of it too, which is what a hook validating the object
148/// wants to put in its message. Neither is worked out until a hook asks, so they cost a deserialization which never
149/// asks nothing.
150///
151/// \section serialization_builder_dsl_serialization Serialization
152///
153/// A type described with the DSL is serialized as an object, written into the \c writer one member at a time in the
154/// order the members were declared: each member's key, then its value through the \c serializer registered for the
155/// member's type. A member whose \ref serialization_builder_dsl_ref_member_level_serialize_if "serialize_if" -- or
156/// the \c since, \c until, \c after and \c before built on it -- says to leave it out is skipped, key and all. The DSL
157/// builds nothing in between, so what a member's serializer writes reaches the sink as it is written. A \c value made
158/// with \c to_json holds the same members sorted by key, as every \c value does, so the text written directly and the
159/// text of that \c value differ in the order of their members and in nothing else. For the \c person above, once the
160/// adapters are combined with the defaults that \c check_references checked them against:
161///
162/// \code
163/// person bob{ "Bob", "Builder", 29, "Foreman" };
164/// jsonv::formats with_defaults = jsonv::formats::compose({ fmts, jsonv::formats::defaults() });
165///
166/// std::string text = jsonv::serialize(bob, with_defaults);
167/// // {"first_name":"Bob","last_name":"Builder","age":29,"role":"Foreman"}
168/// std::string sorted = jsonv::to_string(jsonv::to_json(bob, with_defaults));
169/// // {"age":29,"first_name":"Bob","last_name":"Builder","role":"Foreman"}
170/// \endcode
171///
172/// A member's \c since and \c until ask the \c serialization_context which version is being written, and with no
173/// version every member is written. Serialize through a context made with one to leave out the members it rules out:
174///
175/// \code
176/// jsonv::serialization_context version_1(with_defaults, jsonv::version(1, 0));
177/// std::string before_role = jsonv::serialize(bob, version_1);
178/// // {"first_name":"Bob","last_name":"Builder","age":29}
179/// \endcode
180///
181/// A failure inside a member is reported where it happened: the \c serialization_error carries the member's path, and
182/// a member of a member described with the DSL extends it -- <tt>.address.city</tt> for the \c city of an \c address.
183///
184/// \section Reference
185///
186/// The DSL is made up of three major parts:
187///
188/// 1. \e formats -- modifies a \c jsonv::formats object by adding new type adapters to it
189/// 2. \e type -- modifies the behavior of a \c jsonv::adapter by adding new members to it
190/// 3. \e member -- modifies an individual member inside of a specific type
191///
192/// Each successive function call transforms your context. \e Narrowing calls make your context more specific; for
193/// example, calling \c type from a \e formats context allows you to modify a specific type. \e Widening calls make the
194/// context less specific and are always available; for example, when in the \e member context, you can still call
195/// \c type from the \e formats context to specify a new type.
196///
197/// \dot
198/// digraph serialization_builder_dsl {
199/// formats [label="formats"]
200/// type [label="type"]
201/// member [label="member"]
202///
203/// formats -> formats
204/// formats -> type
205/// type -> formats
206/// type -> type
207/// type -> member
208/// member -> formats
209/// member -> type
210/// member -> member
211/// }
212/// \enddot
213///
214/// \subsection serialization_builder_dsl_ref_formats Formats Context
215///
216/// Commands in this section modify the behavior of the underlying \c jsonv::formats object.
217///
218/// \subsubsection serialization_builder_dsl_ref_formats_level Level
219///
220/// \paragraph serialization_builder_dsl_ref_formats_level_check_references check_references
221///
222/// - <tt>check_references(formats)</tt>
223/// - <tt>check_references(formats, std::string name)</tt>
224/// - <tt>check_references(formats::list)</tt>
225/// - <tt>check_references(formats::list, std::string name)</tt>
226/// - <tt>check_references()</tt>
227/// - <tt>check_references(std::string name)</tt>
228///
229/// Tests that every type referenced by the members of the output of the DSL have a \c deserializer and a \c serializer.
230/// The provided \c formats is used to draw extra types from (a common value is \c jsonv::formats::defaults). In other
231/// words, it asks the question: If the \c formats from this DSL was combined with these other \c formats, could all of
232/// the types be encoded and decoded?
233///
234/// This does not mutate the DSL in any way. On successful verification, it will appear that nothing happened. If the
235/// verification is not successful, an exception will be thrown with the offending types in the message. For example:
236///
237/// \code
238/// There are 2 types referenced that the formats do not know how to serialize:
239/// - date_type (referenced by: name_space::foo, other::name::space::bar)
240/// - tree
241/// \endcode
242///
243/// If \a name is provided, the value will be output to the error message on failure. This can be useful if you have
244/// multiple \c check_references statements and wish to more easily determine the failing \c formats combination from
245/// the error message alone.
246///
247/// \note
248/// This is evaluated \e immediately, so it is best to call this function as the very last step in the DSL.
249///
250/// \code
251/// .check_references(jsonv::formats::defaults())
252/// \endcode
253///
254/// \paragraph serialization_builder_dsl_ref_formats_level_reference_type reference_type
255///
256/// - <tt>reference_type(std::type_index type)</tt>
257/// - <tt>reference_type(std::type_index type, std::type_index from)</tt>
258///
259/// Explicitly add a reference to the provided \a type in the DSL. If \a from is provided, also add a back reference for
260/// tracking purposes. The \a from field is useful for tracking \e why the \a type is referenced.
261///
262/// Type references are used in \ref serialization_builder_dsl_ref_formats_level_check_references to both check and
263/// generate error messages if the \c formats the DSL is building cannot fully create and deserialize JSON values. You
264/// do not usually have to call this, as each call to \ref serialization_builder_dsl_ref_type_narrowing_member calls
265/// this automatically.
266///
267/// \code
268/// .reference_type(std::type_index(typeid(int)), std::type_index(typeid(my_type)))
269/// .reference_type(std::type_index(typeid(my_type))
270/// \endcode
271///
272/// \paragraph serialization_builder_dsl_ref_formats_level_register_adapter register_adapter
273///
274/// - <tt>register_adapter(const adapter*)</tt>
275/// - <tt>register_adapter(std::shared_ptr&lt;const adapter&gt;)</tt>
276///
277/// Register an arbitrary \c adapter with the \c formats we are currently building. This is useful for integrating with
278/// type adapters that do not (or can not) use the DSL.
279///
280/// \code
281/// .register_adapter(my_type::get_adapter())
282/// \endcode
283///
284/// \paragraph serialization_builder_dsl_ref_formats_level_register_optional register_optional
285///
286/// - <tt>register_optional&lt;TOptional&gt;()</tt>
287///
288/// Similar to \c register_adapter, but automatically create an <tt>optional_adapter&lt;TOptional&gt;</tt> to store.
289///
290/// \code
291/// .register_optional<std::optional<int>>()
292/// .register_optional<boost::optional<double>>()
293/// \endcode
294///
295/// \paragraph serialization_builder_dsl_ref_formats_level_register_container register_container
296///
297/// - <tt>register_container&lt;TContainer&gt;()</tt>
298///
299/// Similar to \c register_adapter, but automatically create a <tt>container_adapter&lt;TContainer&gt;</tt> to store.
300///
301/// \code
302/// .register_container<std::vector<int>>()
303/// .register_container<std::list<std::string>>()
304/// \endcode
305///
306/// \paragraph serialization_builder_dsl_ref_formats_level_register_containers register_containers
307///
308/// - <tt>register_containers&lt;T, template &lt;class...&gt; class... TTContainer&gt;</tt>
309///
310/// Convenience function for calling \c register_container for multiple containers with the same \c value_type.
311/// Unfortunately, it only supports varying the first template parameter of the \c TTContainer types, so if you wish to
312/// do something like vary the allocator, you will have to either call \c register_container multiple times or use a
313/// template alias.
314///
315/// \code
316/// .register_containers<int, std::list, std::deque>()
317/// .register_containers<double, std::vector, std::set>()
318/// \endcode
319///
320/// \paragraph serialization_builder_dsl_ref_formats_level_register_wrapper register_wrapper
321///
322/// - <tt>register_wrapper&lt;TWrapper&gt;()</tt>
323///
324/// Similar to \c register_adapter, but automatically create a <tt>wrapper_adapter&lt;TWrapper&gt;</tt> to store.
325/// \c TWrapper names the type it wraps as its member \c value_type, and must be explicitly convertible to and from it.
326///
327/// \code
328/// .register_wrapper<user_id>()
329/// .register_wrapper<temperature>()
330/// \endcode
331///
332/// \paragraph serialization_builder_dsl_ref_formats_level_enum_type enum_type
333///
334/// - <tt>enum_type&lt;TEnum&gt;(std::string name, std::initializer_list&lt;std::pair&lt;TEnum, jsonv::value&gt;&gt;)</tt>
335/// - <tt>enum_type_icase&lt;TEnum&gt;(std::string name, std::initializer_list&lt;std::pair&lt;TEnum, jsonv::value&gt;&gt;)</tt>
336///
337/// Create an adapter for the \c TEnum type with a mapping of C++ values to JSON values and vice versa. The most common
338/// use of this is to map \c enum values in C++ to string representations in JSON. \c TEnum is not restricted to types
339/// which are \c enum, but can be anything which you would like to restrict to a limited subset of possible values.
340/// Likewise, JSON representations are not restricted to being of \c kind::string.
341///
342/// The sibling function \c enum_type_icase will create an adapter which uses case-insensitive checking when converting
343/// to C++ values in \c deserialize.
344///
345/// \code
346/// .enum_type<ring>("ring",
347/// {
348/// { ring::fire, "fire" },
349/// { ring::wind, "wind" },
350/// { ring::earth, "earth" },
351/// { ring::water, "water" },
352/// { ring::heart, "heart" }, // "heart" is preferred for to_json
353/// { ring::heart, "useless" }, // "useless" is read as ring::heart
354/// { ring::fire, 1 }, // the JSON value 1 is also read as ring::fire
355/// { ring::ussr, "wind" }, // old C++ value ring::ussr will get output as "wind"
356/// }
357/// )
358/// .enum_type_icase<int>("integer",
359/// {
360/// { 0, "zero" },
361/// { 0, "naught" },
362/// { 1, "one" },
363/// { 2, "two" },
364/// { 3, "three" },
365/// }
366/// )
367/// \endcode
368///
369/// \see enum_adapter
370///
371/// \paragraph serialization_builder_dls_ref_formats_level_polymorphic_type polymorphic_type
372///
373/// - <tt>polymorphic_type<&lt;TPointer&gt;(std::string discrimination_key);</tt>
374///
375/// Create an adapter for the \c TPointer type (usually \c std::shared_ptr or \c std::unique_ptr) that knows how to
376/// serialize and deserialize one or more types that can be polymorphically represented by \c TPointer, i.e. derived
377/// types. It uses a discrimination key to determine which concrete type should be instantiated when deserializing
378/// values from json.
379///
380/// \code
381/// .polymorphic_type<std::unique_ptr<base>>("type")
382/// .subtype<derived_1>("derived_1")
383/// .subtype<derived_2>("derived_2", keyed_subtype_action::check)
384/// .subtype<derived_3>("derived_3", keyed_subtype_action::insert);
385/// \endcode
386///
387/// The \ref keyed_subtype_action can be used to configure the adapter to make sure that the discrimination key was
388/// correctly serialized (\ref keyed_subtype_action::check) or to insert the discrimination key for the underlying type
389/// so that the underlying type doesn't need to do that itself (\ref keyed_subtype_action::insert). The default is to do
390/// nothing (\ref keyed_subtype_action::none). Either action needs the finished object, so it builds the subtype as a
391/// \c value before writing it, and that subtype's members are written sorted by key rather than in the order they were
392/// declared.
393///
394/// \paragraph serialization_builder_dsl_ref_formats_level_extend extend
395///
396/// - <tt>extend(std::function&lt;void (formats_builder&amp;)&gt; func)</tt>
397///
398/// Extend the \c formats_builder with the provided \a func by passing the current builder to it. This provides a more
399/// convenient way to call helper functions.
400///
401/// \code
402/// jsonv::formats_builder builder;
403/// foo(builder);
404/// bar(builder);
405/// baz(builder);
406/// \endcode
407///
408/// This can be done equivalently with:
409/// \code
410/// jsonv::formats_builder()
411/// .extend(foo)
412/// .extend(bar)
413/// .extend(baz)
414/// \endcode
415///
416/// \paragraph serialization_builder_dsl_ref_formats_level_on_duplicate_type on_duplicate_type
417///
418/// - <tt>on_duplicate_type(on_duplicate_type_action action);</tt>
419///
420/// Set what action to take when attempting to register an adapter, but there is already an adapter for that type in the
421/// formats. The default is to throw a \ref duplicate_type_error exception (\ref duplicate_type_action::exception), but
422/// the \c formats_builder can also be configured to ignore the duplicate (\ref duplicate_type_action::ignore), or to
423/// replace the existing adapter with the new one (\ref duplicate_type_action::replace). This is useful when calling
424/// multiple \c extend methods that may add common types to the \c formats_builder.
425///
426/// \subsubsection serialization_builder_dsl_ref_formats_narrowing Narrowing
427///
428/// \paragraph serialization_builder_dsl_ref_formats_narrowing_type type&lt;T&gt;
429///
430/// - <tt>type&lt;T&gt;()</tt>
431/// - <tt>type&lt;T&gt;(std::function&lt;void (adapter_builder&lt;T&gt;&amp;)&gt; func)</tt>
432///
433/// Create an \c adapter for type \c T and begin building the members for it. If \a func is provided, it will be called
434/// with the adapter_builder&lt;T&gt; this call to \c type creates, which can be used for creating common extension
435/// functions.
436///
437/// \code
438/// .type<my_type>()
439/// .member(...)
440/// .
441/// .
442/// .
443/// \endcode
444///
445///
446/// \subsection serialization_builder_dsl_ref_type Type Context
447///
448/// Commands in this section modify the behavior of the \c jsonv::adapter for a particular type.
449///
450/// \subsubsection serialization_builder_dsl_ref_type_level Level
451///
452/// \paragraph serialization_builder_dsl_ref_type_level_pre_deserialize pre_deserialize
453///
454/// - <tt>pre_deserialize(std::function&lt;void (deserialization_context& context)&gt; perform)</tt>
455///
456/// Call the given \a perform function during the \c deserialize operation, but before performing any deserialization.
457/// This can be called multiple times -- all functions will be called in the order they are provided.
458///
459/// The source document is not among the arguments, but \c deserialization_context::source_value has it, before any
460/// member has been read out of it. Nothing yet knows that it is an object, so it is shown as whatever it is --
461/// including a \c null that \ref serialization_builder_dsl_ref_type_level_type_default_on_null "type_default_on_null"
462/// goes on to replace. Its text cannot be quoted yet, so \c deserialization_context::encoded_source is empty.
463///
464/// \code
465/// .type<my_type>()
466/// .pre_deserialize([] (deserialization_context& context)
467/// {
468/// if (context.source_value().value().at("schema") != jsonv::value(2))
469/// throw std::invalid_argument("Only schema 2 is supported");
470/// }
471/// )
472/// \endcode
473///
474/// \paragraph serialization_builder_dsl_ref_type_level_post_deserialize post_deserialize
475///
476/// - <tt>post_deserialize(std::function&lt;T (deserialization_context& context, T&& out)&gt; perform)</tt>
477///
478/// Call the given \a perform function after the \c deserialize operation. All functions will be called in the order
479/// they are provided. This allows validation methods to be called on the deserialized object as part of
480/// deserialization. Postprocessing functions are allowed to mutate the deserialized object.
481///
482/// The JSON the object was read from is there to read, through \c deserialization_context::source_value, and a
483/// validation which fails can quote it through \c deserialization_context::encoded_source:
484///
485/// \code
486/// .type<my_type>()
487/// .member("low", &my_type::low)
488/// .member("high", &my_type::high)
489/// .post_deserialize([] (deserialization_context& context, my_type&& out)
490/// {
491/// if (out.high < out.low)
492/// throw std::invalid_argument("high is below low in "
493/// + std::string(context.encoded_source())
494/// );
495///
496/// return std::move(out);
497/// }
498/// )
499/// \endcode
500///
501/// \paragraph serialization_builder_dsl_ref_type_level_type_default_on_null type_default_on_null
502///
503/// - <tt>type_default_on_null()</tt>
504/// - <tt>type_default_on_null(bool on)</tt>
505///
506/// If the JSON value \c null is in the input, should this type take on some default? This option is only considered
507/// if a \ref serialization_builder_dsl_ref_type_level_type_default_value was provided.
508///
509/// \paragraph serialization_builder_dsl_ref_type_level_type_default_value type_default_value
510///
511/// - <tt>type_default_value(T value)</tt>
512/// - <tt>type_default_value(std::function&lt;T (deserialization_context& context)&gt;)</tt>
513///
514/// What value should be used to create the default for this type? It stands in for the \c null rather than reading it,
515/// so \c deserialization_context::source_value shows it nothing.
516///
517/// \code
518/// .type<my_type>()
519/// .type_default_on_null()
520/// .type_default_value(my_type("default"))
521/// \endcode
522///
523/// \paragraph serialization_builder_dsl_ref_type_level_on_unknown_members on_unknown_members
524///
525/// - <tt>on_unknown_members(std::function&lt;void (deserialization_context& context,
526/// std::set&lt;std::string&gt; unknown_members)&gt; action
527/// )</tt>
528///
529/// When deserializing, perform some \a action if the object has members which no declared member claims. By default
530/// they are simply ignored, so this is useful if you wish to throw an exception (or anything you want). The \a action
531/// is handed the names of the keys which claimed no member. The walk stepped over their values rather than reading
532/// them, but the \a action can read them out of \c deserialization_context::source_value, or quote the object they
533/// are in with \c deserialization_context::encoded_source.
534///
535/// \code
536/// .type<my_type>()
537/// .member("x", &my_type::x)
538/// .member("y", &my_type::y)
539/// .on_unknown_members([] (deserialization_context&, std::set<std::string> unknown_members)
540/// {
541/// throw unknown_members_error("my_type", std::move(unknown_members));
542/// }
543/// )
544/// \endcode
545///
546/// There is a convenience function named \c deny_unknown_members which does this for you.
547///
548/// \code
549/// .type<my_type>()
550/// .member("x", &my_type::x)
551/// .member("y", &my_type::y)
552/// .on_unknown_members(jsonv::deny_unknown_members)
553/// \endcode
554///
555/// \subsubsection serialization_builder_dsl_ref_type_narrowing Narrowing
556///
557/// \paragraph serialization_builder_dsl_ref_type_narrowing_member member
558///
559/// - <tt>member(std::string name, TMember T::*selector)</tt>
560/// - <tt>member(std::string name, const TMember& (*access)(const T&), void (*mutate)(T&, TMember&&))</tt>
561/// - <tt>member(std::string name, const TMember& (T::*access)() const, TMember& (T::*mutable_access)())</tt>
562/// - <tt>member(std::string name, const TMember& (T::*access)() const, void (T::*mutate)(TMember))</tt>
563/// - <tt>member(std::string name, const TMember& (T::*access)() const, void (T::*mutate)(TMember&&))</tt>
564///
565/// Adds a member to the type we are currently building. By default, the member will be serialized with the key of the
566/// given \a name and the deserializer will search for the given \a name. If you wish to change properties of this
567/// field, use the \ref serialization_builder_dsl_ref_member. Members are written in the order they are declared, and
568/// declaring a second member with a \a name already declared for the type throws \c std::invalid_argument.
569///
570/// \code
571/// .type<my_type>()
572/// .member("x", &my_type::x)
573/// .member("y", &my_type::y)
574/// .member("thing", &my_type::get_thing, &my_type::set_thing)
575/// \endcode
576///
577///
578/// \subsection serialization_builder_dsl_ref_member Member Context
579///
580/// Commands in this section modify the behavior of a particular member. Here, \c T refers to the containing type (the
581/// one we are adding a member to) and \c TMember refers to the type of the member we are modifying.
582///
583/// \subsubsection serialization_builder_dsl_ref_member_level Level
584///
585/// \paragraph serialization_builder_dsl_ref_member_level_after after
586///
587/// - <tt>after(version)</tt>
588///
589/// Only serialize this member if the \c serialization_context was not created with a version, or its version is
590/// greater than the provided \c version.
591///
592/// \paragraph serialization_builder_dsl_ref_member_level_alias alias
593///
594/// - <tt>alias(std::string name)</tt>
595///
596/// Provide another name to look for when deserializing this member. If a document provides values under more than one
597/// of a member's names, the earliest declared is preferred, starting with the name the member was declared with.
598///
599/// \paragraph serialization_builder_dsl_ref_member_level_before before
600///
601/// - <tt>before(version)</tt>
602///
603/// Only serialize this member if the \c serialization_context was not created with a version, or its version is
604/// less than the provided \c version.
605///
606/// \paragraph serialization_builder_dsl_ref_member_level_check check
607///
608/// - <tt>check(std::function&lt;void (const TMember&)&gt; inspect)</tt>
609/// - <tt>check(std::function&lt;bool (const TMember&)&gt; accept,
610/// std::function&lt;void (const TMember&)&gt; thrower
611/// )</tt>
612/// - <tt>check(std::function&lt;bool (const TMember&)&gt; accept, TException ex)</tt>
613///
614/// Checks the value read for this member before it reaches it. In the first form, \a inspect is expected to throw
615/// for itself. In the latter forms, a value \a accept returns \c false for is handed to \a thrower, or \a ex is
616/// thrown directly; \a ex may be anything which cannot be called with the value, which is what tells it from a
617/// \a thrower.
618///
619/// \code
620/// .member("x", &my_type::x)
621/// .check([] (int x) { if (x < 0) throw std::logic_error("x must not be negative"); })
622/// .check([] (int x) { return x < 100; },
623/// [] (int x) { throw std::out_of_range("x must be below 100, not " + std::to_string(x)); }
624/// )
625/// .check([] (int x) { return x % 2 == 0; }, std::logic_error("x must be divisible by 2"))
626/// \endcode
627///
628/// \paragraph serialization_builder_dsl_ref_member_level_default_value default_value
629///
630/// - <tt>default_value(TMember value)</tt>
631/// - <tt>default_value(std::function&lt;TMember (deserialization_context& context)&gt; create)</tt>
632///
633/// Provide a default value for this member if no key is found when deserializing. The function implementation can
634/// synthesize the value however it likes. A missing key is only known to be missing once every key which was there has
635/// gone by, so defaults are taken once the walk is done -- including one standing in for a \c null under \ref
636/// serialization_builder_dsl_ref_member_level_default_on_null "default_on_null" -- and the function can read the rest
637/// of the object through \c deserialization_context::source_value, or quote it through \c
638/// deserialization_context::encoded_source. What it reads is the JSON. A default which depends on the members as they
639/// were deserialized belongs in \ref serialization_builder_dsl_ref_type_level_post_deserialize, which sees the whole
640/// object once it is built.
641///
642/// \code
643/// .member("x", &my_type::x)
644/// .default_value(10)
645/// .member("name", &my_type::name)
646/// .member("display_name", &my_type::display_name)
647/// .default_value([] (deserialization_context& context)
648/// {
649/// return context.source_value().value().at("name").as_string();
650/// }
651/// )
652/// \endcode
653///
654/// \paragraph serialization_builder_dsl_ref_member_level_default_on_null default_on_null
655///
656/// - <tt>default_on_null()</tt>
657/// - <tt>default_on_null(bool on)</tt>
658///
659/// If the value associated with this key is \c kind::null, should that be treated as though the key were missing, and
660/// the default value taken? This option is only considered if a
661/// \ref serialization_builder_dsl_ref_member_level_default_value was provided.
662///
663/// \paragraph serialization_builder_dsl_ref_member_level_serialize_if serialize_if
664///
665/// - <tt>serialize_if(std::function&lt;bool (const serialization_context&, const TMember&amp;)&gt; check)</tt>
666///
667/// Only serialize this member if the \a check function returns true.
668///
669/// \paragraph serialization_builder_dsl_ref_member_level_since since
670///
671/// - <tt>since(version)</tt>
672///
673/// Only serialize this member if the \c serialization_context was not created with a version, or its version is
674/// greater than or equal to the provided \c version.
675///
676/// \paragraph serialization_builder_dsl_ref_member_level_until until
677///
678/// - <tt>until(version)</tt>
679///
680/// Only serialize this member if the \c serialization_context was not created with a version, or its version is
681/// less than or equal to the provided \c version.
682
683class formats_builder;
684
685template <typename T> class adapter_builder;
686template <typename T, typename TMember> class member_adapter_builder;
687template <typename TPointer> class polymorphic_adapter_builder;
688
689namespace detail
690{
691
692class formats_builder_dsl
693{
694public:
695 explicit formats_builder_dsl(formats_builder* owner) :
696 owner(owner)
697 { }
698
699 template <typename T>
700 adapter_builder<T> type();
701
702 template <typename T, typename F>
703 adapter_builder<T> type(F&&);
704
705 template <typename TEnum>
706 formats_builder& enum_type(std::string enum_name, std::initializer_list<std::pair<TEnum, value>> mapping);
707
708 template <typename TEnum>
709 formats_builder& enum_type_icase(std::string enum_name, std::initializer_list<std::pair<TEnum, value>> mapping);
710
711 template <typename TPointer>
712 polymorphic_adapter_builder<TPointer> polymorphic_type(std::string discrimination_key = "");
713
714 template <typename TPointer, typename F>
715 polymorphic_adapter_builder<TPointer> polymorphic_type(std::string discrimination_key, F&&);
716
717 template <typename F>
718 formats_builder& extend(F&&);
719
720 formats_builder& register_adapter(const adapter* p);
721 formats_builder& register_adapter(std::shared_ptr<const adapter> p);
722
723 formats_builder& reference_type(std::type_index typ);
724 formats_builder& reference_type(std::type_index type, std::type_index from);
725
726 template <typename TOptional>
727 formats_builder& register_optional();
728
729 template <typename TContainer>
730 formats_builder& register_container();
731
732 template <typename T, template <class...> class... TTContainers>
733 formats_builder& register_containers();
734
735 template <typename TWrapper>
736 formats_builder& register_wrapper();
737
738 formats_builder& check_references(const formats& other, const std::string& name = "");
739 formats_builder& check_references(const formats::list& others, const std::string& name = "");
740 formats_builder& check_references(const std::string& name = "");
741
742 formats_builder& on_duplicate_type(duplicate_type_action action) noexcept;
743
745 formats compose_checked(formats other, const std::string& name = "");
747 formats compose_checked(std::vector<formats> others, const std::string& name = "");
748
750 operator formats() const;
751
752protected:
753 formats_builder* owner;
754};
755
756template <typename T>
757class adapter_builder_dsl
758{
759public:
760 explicit adapter_builder_dsl(adapter_builder<T>* owner) :
761 owner(owner)
762 { }
763
764 adapter_builder<T>& type_default_on_null(bool on = true);
765
766 adapter_builder<T>& type_default_value(std::function<T (deserialization_context& ctx)> create);
767
768 adapter_builder<T>& type_default_value(const T& value);
769
770 template <typename TMember>
771 member_adapter_builder<T, TMember> member(std::string name, TMember T::*selector);
772
773 template <typename TMember>
774 member_adapter_builder<T, TMember> member(std::string name,
775 std::function<const TMember& (const T&)> access,
776 std::function<void (T&, TMember&&)> mutate
777 );
778
779 template <typename TMember>
780 member_adapter_builder<T, TMember> member(std::string name,
781 const TMember& (T::*access)() const,
782 TMember& (T::*mutable_access)()
783 );
784
785 template <typename TMember>
786 member_adapter_builder<T, TMember> member(std::string name,
787 const TMember& (T::*access)() const,
788 void (T::*mutate)(TMember)
789 );
790
791 template <typename TMember>
792 member_adapter_builder<T, TMember> member(std::string name,
793 const TMember& (T::*access)() const,
794 void (T::*mutate)(TMember&&)
795 );
796
797 adapter_builder<T>& pre_deserialize(typename adapter_builder<T>::pre_deserialize_func perform);
798
799 adapter_builder<T>& post_deserialize(typename adapter_builder<T>::post_deserialize_func perform);
800
801 adapter_builder<T>& on_unknown_members(typename adapter_builder<T>::unknown_members_func handler);
802
803protected:
804 adapter_builder<T>* owner;
805};
806
807/// Is the value \a from is positioned on a JSON `null`?
808///
809/// Asked of the lent \c value where there is one, exactly as \c optional_adapter does and for the same reason: a
810/// value-backed reader has no token for a non-finite \c kind::decimal and renders one as \c literal_null, so going by
811/// the node type alone would call a \c double holding a NaN "null" and take a default for it.
813inline bool current_is_null(const reader& from)
814{
815 if (auto lent = from.current_value())
816 return lent->kind() == jsonv::kind::null;
817 else
818 return from.good() && from.current_type() == ast_node_type::literal_null;
819}
820
821/// The answer \c member_adapter::deserialize_key_rank gives for a key which names no member.
822constexpr std::size_t no_deserialize_key = std::size_t(-1);
823
824/// Which member each key has claimed, and by which of that member's names.
825///
826/// A member answers to the name it was declared with and to every \c alias after it, and that list is a
827/// preference order. Remembering only *that* a member was claimed cannot tell a second spelling of it from a repeat
828/// of the first, which is a different question with a different answer: the first is the document naming one member
829/// two ways, where the earliest name wins, and the second is a duplicate key, which is
830/// \c deserialize_options::on_duplicate_key's to decide.
831///
832/// A key whose \c null stands for the member's default under \c default_on_null claims it all the same, and is
833/// remembered as having done so: the default is applied once the walk is done, alongside the defaults of the members
834/// no key claimed.
835///
836/// Read once the walk is done, so it has to be cheap to make: a wholly successful deserialization of an object
837/// allocates nothing, and a `std::vector` would spoil that for every object deserialized. Types with more members than
838/// fit inline fall back to one.
839class member_claim_set
840{
841public:
842 /// No key has claimed this member.
843 static constexpr std::uint16_t unclaimed = std::uint16_t(-1);
844
845public:
846 explicit member_claim_set(std::size_t count)
847 {
848 if (count > inline_capacity)
849 {
850 _spilled = std::make_unique<spilled_claim[]>(count);
851 std::fill_n(_spilled.get(), count, spilled_claim{ unclaimed, false });
852 }
853 else
854 {
855 _ranks.fill(unclaimed);
856 }
857 }
858
859 /// Which of member \a idx's names claimed it, or \ref unclaimed if none has.
861 std::uint16_t claim_rank(std::size_t idx) const
862 {
863 return _spilled ? _spilled[idx].rank : _ranks[idx];
864 }
865
867 bool claimed(std::size_t idx) const
868 {
869 return claim_rank(idx) != unclaimed;
870 }
871
872 /// Is member \a idx to be given its default once the walk is done? It is if no key claimed it, and if the key
873 /// which last did held a \c null standing for the default.
875 bool wants_default(std::size_t idx) const
876 {
877 if (!claimed(idx))
878 return true;
879 else if (_spilled)
880 return _spilled[idx].null_default;
881 else
882 return (_null_defaults >> idx & 1U) != 0U;
883 }
884
885 /// Record that the name at \a rank is the one member \a idx is being read from -- or, if \a null_default, that
886 /// the key under it held a \c null which stands for the member's default.
887 void claim(std::size_t idx, std::size_t rank, bool null_default = false)
888 {
889 // A member with more names than this can count is not something anyone builds; the clamp is here so the type
890 // can stay narrow, not because the case is expected.
891 auto stored = std::uint16_t(std::min<std::size_t>(rank, unclaimed - 1U));
892
893 if (_spilled)
894 {
895 _spilled[idx] = spilled_claim{ stored, null_default };
896 }
897 else
898 {
899 _ranks[idx] = stored;
900
901 const auto bit = std::uint64_t(1U) << idx;
902 _null_defaults = null_default ? (_null_defaults | bit) : (_null_defaults & ~bit);
903 }
904 }
905
906private:
907 static constexpr std::size_t inline_capacity = 64U;
908 static_assert(inline_capacity <= 64U, "_null_defaults has a bit for each member tracked inline");
909
910 /// A claim on a member of a type too big to track inline.
911 struct spilled_claim
912 {
913 std::uint16_t rank;
914 bool null_default;
915 };
916
917 /// A plain array rather than a `std::vector`, which allocates a debugging proxy on some standard libraries even
918 /// when it is empty -- and this one is empty for every type small enough to be tracked inline, which is nearly
919 /// all of them.
920 std::array<std::uint16_t, inline_capacity> _ranks;
921
922 /// For each member tracked inline, whether the key which claimed it held a \c null standing for its default. Kept
923 /// apart from the rank rather than as a bit of it, so that no rank and no claim can be mistaken for \ref unclaimed.
924 std::uint64_t _null_defaults = 0U;
925 std::unique_ptr<spilled_claim[]> _spilled;
926};
927
928template <typename T>
929class member_adapter
930{
931public:
932 virtual ~member_adapter() noexcept
933 { }
934
935 /// Deserialize this member from \a from, which is positioned on the member's value, and set it on \a out. On return
936 /// the cursor sits one position past that value, as every deserializer owes its caller. A \c null which
937 /// \ref defaults_on_null says stands for the default never reaches this; the walk keeps it for the default.
938 ///
939 /// \param key The key the document used, which is what names this member in a problem raised inside it -- the
940 /// declared name would be the wrong answer for a member matched through an \c alias. It must
941 /// outlive the call, which the caller arranges: a canonical key views the source, and an escaped one
942 /// views the \c std::string the key loop decoded it into.
944 virtual std::expected<void, ast_node_type>
945 deserialize(deserialization_context& context, reader& from, std::string_view key, T& out) const = 0;
946
947 /// Apply this member's default to \a out, reading nothing. Only called on a member which \ref has_default, once the
948 /// walk is done: when no key claimed it, and when the key which did held \c null and \ref defaults_on_null.
950 virtual std::expected<void, ast_node_type> apply_default(deserialization_context& context, T& out) const = 0;
951
952 /// Does a \c null under this member's key stand for its default? Only with a default to take: without one, a
953 /// \c null is a value like any other and goes to \ref deserialize, just as a missing key without one is reported
954 /// missing rather than defaulted.
956 virtual bool defaults_on_null() const = 0;
957
958 /// Write this member of \a from into \a to, which is inside the open object the member belongs to: its key, then
959 /// its value -- or nothing at all, when a \c serialize_if (or the \c since, \c until, \c after and \c before built
960 /// on it) leaves the member out. A failure writing the value is reported at the member's path, since that is where
961 /// the writer is standing.
962 virtual void serialize(const serialization_context& context, const T& from, writer& to) const = 0;
963
964 /// Does this member have a default to fall back on? A member without one is required.
966 virtual bool has_default() const = 0;
967
968 /// Where \a key sits in this member's list of names -- 0 for the name it was declared with, then one for each \c
969 /// alias in the order they were added -- or \ref no_deserialize_key if this member does not answer to it.
970 ///
971 /// That position is the preference order `alias` documents, and a forward walk has to enforce it for
972 /// itself: it meets the names in the order the *document* put them, which says nothing about which one the type
973 /// prefers.
975 virtual std::size_t deserialize_key_rank(std::string_view key) const = 0;
976
977 /// The name this member was declared with, for naming it in a problem raised where the document's own key is not
978 /// to hand. It is owned by this adapter and outlives any deserialization, so it is safe to push as a `path_scope`.
980 virtual std::string_view primary_name() const = 0;
981};
982
983template <typename T, typename TMember>
984class member_adapter_impl :
985 public member_adapter<T>
986{
987public:
988 using mutator_type = std::function<void (T&, TMember&&)>;
989 using accessor_type = std::function<const TMember& (const T&)>;
990
991public:
992 explicit member_adapter_impl(std::string name, mutator_type mutator, accessor_type access) :
993 _names({ std::move(name) }),
994 _set_value(std::move(mutator)),
995 _get_value(std::move(access))
996 { }
997
998 explicit member_adapter_impl(std::string name, TMember T::*selector) :
999 member_adapter_impl(std::move(name),
1000 [selector] (T& value, TMember&& x) { value.*selector = std::move(x); },
1001 [selector] (const T& value) -> const TMember& { return value.*selector; }
1002 )
1003 { }
1004
1006 virtual std::expected<void, ast_node_type>
1007 deserialize(deserialization_context& context, reader& from, std::string_view key, T& out) const override
1008 {
1009 // Scoped to the deserialization and not to the assignment. `_set_value` is whatever the `member(name, access,
1010 // mutate)` overload was handed, so it is arbitrary user code, and once the member's value exists the
1011 // deserializer is no longer at this key -- something that setter goes on to deserialize is where the document
1012 // says it is rather than underneath this member.
1013 //
1014 // `key` is the key the document actually used, which is the one to name when a member matched through an
1015 // `alias`. The caller keeps it alive across this call, so naming it costs nothing.
1016 auto deserialized = [&] () -> std::expected<TMember, ast_node_type>
1017 {
1018 deserialization_context::path_scope scope(context, key);
1019
1020 return context.deserialize<TMember>(from);
1021 }();
1022
1023 if (!deserialized)
1024 return std::unexpected(deserialized.error());
1025
1026 // `check` runs on the value which was read, before it reaches the setter. It throws rather than
1027 // reporting, so it is user code the loop above has to be ready for.
1028 if (_deserialize_mutate)
1029 _set_value(out, _deserialize_mutate(*std::move(deserialized)));
1030 else
1031 _set_value(out, *std::move(deserialized));
1032
1033 return {};
1034 }
1035
1037 virtual std::expected<void, ast_node_type> apply_default(deserialization_context& context, T& out) const override
1038 {
1039 _set_value(out, _default_value(context));
1040 return {};
1041 }
1042
1043 virtual void serialize(const serialization_context& context, const T& from, writer& to) const override
1044 {
1045 const TMember& member = _get_value(from);
1046 if (!_should_serialize || _should_serialize(context, member))
1047 {
1048 to.key(_names.at(0));
1049 context.serialize(member, to);
1050 }
1051 }
1052
1054 virtual bool has_default() const override
1055 {
1056 return bool(_default_value);
1057 }
1058
1060 virtual bool defaults_on_null() const override
1061 {
1062 return _default_on_null && _default_value;
1063 }
1064
1066 virtual std::size_t deserialize_key_rank(std::string_view key) const override
1067 {
1068 for (std::size_t rank = 0U; rank < _names.size(); ++rank)
1069 if (_names[rank] == key)
1070 return rank;
1071
1072 return no_deserialize_key;
1073 }
1074
1076 virtual std::string_view primary_name() const override
1077 {
1078 return _names.at(0);
1079 }
1080
1081 void add_serialize_check(std::function<bool (const serialization_context&, const TMember&)> check)
1082 {
1083 if (_should_serialize)
1084 {
1085 auto old_check = std::move(_should_serialize);
1086 _should_serialize = [check, old_check] (const serialization_context& context, const TMember& value)
1087 {
1088 return check(context, value) && old_check(context, value);
1089 };
1090 }
1091 else
1092 {
1093 _should_serialize = std::move(check);
1094 }
1095 }
1096
1097 void add_deserialization_mutator(std::function <TMember (TMember&&)> mutate)
1098 {
1099 if (_deserialize_mutate)
1100 {
1101 auto old_mutate = std::move(_deserialize_mutate);
1102 _deserialize_mutate = [old_mutate, mutate] (TMember&& member)
1103 {
1104 return mutate(old_mutate(std::move(member)));
1105 };
1106 }
1107 else
1108 {
1109 _deserialize_mutate = std::move(mutate);
1110 }
1111 }
1112
1113 void add_deserialization_check(std::function <void (const TMember&)> check)
1114 {
1115 add_deserialization_mutator([check] (TMember&& value)
1116 {
1117 check(value);
1118 return value;
1119 });
1120 }
1121
1122 void default_value(std::function<TMember (deserialization_context&)>&& create)
1123 {
1124 _default_value = std::move(create);
1125 }
1126
1127 void default_on_null(bool on)
1128 {
1129 _default_on_null = on;
1130 }
1131
1132private:
1133 // Qualified: unqualified, this declares a friend `jsonv::detail::member_adapter_builder`, which is a different
1134 // (and nonexistent) template from the `jsonv::member_adapter_builder` which actually reaches in here. Every use
1135 // of `alias` failed to compile until this was spelled out.
1136 template <typename U, typename UMember>
1137 friend class jsonv::member_adapter_builder;
1138
1139private:
1140 std::vector<std::string> _names;
1141 mutator_type _set_value;
1142 accessor_type _get_value;
1143 std::function<bool (const serialization_context&, const TMember&)> _should_serialize;
1144 std::function<TMember (deserialization_context&)> _default_value;
1145 bool _default_on_null = false;
1146 std::function<TMember (TMember&&)> _deserialize_mutate;
1147};
1148
1149}
1150
1151template <typename T, typename TMember>
1153 public detail::formats_builder_dsl,
1154 public detail::adapter_builder_dsl<T>
1155{
1156public:
1157 explicit member_adapter_builder(formats_builder* fmt_builder,
1158 adapter_builder<T>* adapt_builder,
1159 detail::member_adapter_impl<T, TMember>* adapter
1160 ) :
1161 formats_builder_dsl(fmt_builder),
1162 detail::adapter_builder_dsl<T>(adapt_builder),
1163 _adapter(adapter)
1164 {
1165 reference_type(std::type_index(typeid(TMember)), std::type_index(typeid(T)));
1166 }
1167
1168 /** When deserializing, also look for this \a name as a key. **/
1170 {
1171 _adapter->_names.emplace_back(std::move(name));
1172 return *this;
1173 }
1174
1175 member_adapter_builder& check(std::function<void (const TMember&)> inspect)
1176 {
1177 _adapter->add_deserialization_check(std::move(inspect));
1178 return *this;
1179 }
1180
1181 member_adapter_builder& check(std::function<bool (const TMember&)> accept,
1182 std::function<void (const TMember&)> thrower
1183 )
1184 {
1185 _adapter->add_deserialization_check([accept = std::move(accept), thrower = std::move(thrower)]
1186 (const TMember& value)
1187 {
1188 if (!accept(value))
1189 thrower(value);
1190 });
1191 return *this;
1192 }
1193
1194 /// Constrained to a \c TException which cannot be called with the value as a non-const lvalue, which is how
1195 /// \c std::function calls its target, so a \c mutable lambda counts as callable. Without the constraint every
1196 /// \a thrower lambda would land here, an exact match for \c TException where the overload above needs a
1197 /// conversion to \c std::function, and be thrown instead of called.
1198 template <typename TException>
1199 requires (!std::invocable<TException&, const TMember&>)
1200 member_adapter_builder& check(std::function<bool (const TMember&)> accept, TException ex)
1201 {
1202 return check(std::move(accept), [ex = std::move(ex)] (const TMember&) { throw ex; });
1203 }
1204
1205 /** If the key for this member is not in the object when deserializing, call this function to create a value. If a
1206 * \c default_value is not specified, the key is required.
1207 **/
1209 {
1210 _adapter->default_value(std::move(create));
1211 return *this;
1212 }
1213
1214 /** If the key for this member is not in the object when deserializing, use this \a value. If a \c default_value is
1215 * not specified, the key is required.
1216 **/
1218 {
1219 return default_value([value] (deserialization_context&) { return value; });
1220 }
1221
1222 /** Should a \c kind::null for a key be interpreted as a missing value? Only considered if a \c default_value was
1223 * provided; without one, the \c null is deserialized like any other value.
1224 **/
1226 {
1227 _adapter->default_on_null(on);
1228 return *this;
1229 }
1230
1231 /** Only serialize this member if the \a check passes. The final decision to serialize is based on \e all \c check
1232 * functions.
1233 **/
1234 member_adapter_builder& serialize_if(std::function<bool (const serialization_context&, const TMember&)> check)
1235 {
1236 _adapter->add_serialize_check(std::move(check));
1237 return *this;
1238 }
1239
1240 /// Only serialize this member if the \c serialization_context was not created with a version, or its version
1241 /// is greater than or equal to \a ver.
1243 {
1244 return serialize_if([ver] (const serialization_context& context, const TMember&)
1245 {
1246 return !context.version() || *context.version() >= ver;
1247 }
1248 );
1249 }
1250
1251 /// Only serialize this member if the \c serialization_context was not created with a version, or its version
1252 /// is less than or equal to \a ver.
1254 {
1255 return serialize_if([ver] (const serialization_context& context, const TMember&)
1256 {
1257 return !context.version() || *context.version() <= ver;
1258 }
1259 );
1260 }
1261
1262 /// Only serialize this member if the \c serialization_context was not created with a version, or its version
1263 /// is greater than \a ver.
1265 {
1266 return serialize_if([ver] (const serialization_context& context, const TMember&)
1267 {
1268 return !context.version() || *context.version() > ver;
1269 }
1270 );
1271 }
1272
1273 /// Only serialize this member if the \c serialization_context was not created with a version, or its version
1274 /// is less than \a ver.
1276 {
1277 return serialize_if([ver] (const serialization_context& context, const TMember&)
1278 {
1279 return !context.version() || *context.version() < ver;
1280 }
1281 );
1282 }
1283
1284private:
1285 detail::member_adapter_impl<T, TMember>* _adapter;
1286};
1287
1288template <typename T>
1290 public detail::formats_builder_dsl
1291{
1292public:
1293 using pre_deserialize_func = std::function<void (deserialization_context&)>;
1294 using post_deserialize_func = std::function<T (deserialization_context&, T&&)>;
1295 using unknown_members_func = std::function<void (deserialization_context&, std::set<std::string>)>;
1296
1297public:
1298 template <typename F>
1299 explicit adapter_builder(formats_builder* owner, F&& f) :
1300 formats_builder_dsl(owner),
1301 _adapter(nullptr)
1302 {
1303 auto adapter = std::make_shared<adapter_impl>();
1304 register_adapter(adapter);
1305 _adapter = adapter.get();
1306
1307 std::forward<F>(f)(*this);
1308 }
1309
1310 explicit adapter_builder(formats_builder* owner) :
1311 adapter_builder(owner, [] (const adapter_builder<T>&) { })
1312 { }
1313
1314 adapter_builder<T>& type_default_on_null(bool on = true)
1315 {
1316 _adapter->_default_on_null = on;
1317 return *this;
1318 }
1319
1320 adapter_builder<T>& type_default_value(std::function<T (deserialization_context& ctx)> create)
1321 {
1322 _adapter->_create_default = std::move(create);
1323 return *this;
1324 }
1325
1326 adapter_builder<T>& type_default_value(const T& value)
1327 {
1328 return type_default_value([value] (deserialization_context&) { return T(value); });
1329 }
1330
1331 /// \throws std::invalid_argument if a member with this \a name is already declared for \c T.
1332 template <typename TMember>
1333 member_adapter_builder<T, TMember> member(std::string name, TMember T::*selector)
1334 {
1335 return add_member(std::make_unique<detail::member_adapter_impl<T, TMember>>(std::move(name), selector));
1336 }
1337
1338 /// \throws std::invalid_argument if a member with this \a name is already declared for \c T.
1339 template <typename TMember>
1341 std::function<const TMember& (const T&)> access,
1342 std::function<void (T&, TMember&&)> mutate
1343 )
1344 {
1345 return add_member(std::make_unique<detail::member_adapter_impl<T, TMember>>(std::move(name),
1346 std::move(mutate),
1347 std::move(access)
1348 ));
1349 }
1350
1351 template <typename TMember>
1353 const TMember& (T::*access)() const,
1354 TMember& (T::*mutable_access)()
1355 )
1356 {
1357 return member<TMember>(std::move(name),
1358 access,
1359 [mutable_access] (T& x, TMember&& val) { (x.*mutable_access)() = std::move(val); }
1360 );
1361 }
1362
1363 template <typename TMember>
1364 member_adapter_builder<T, TMember> member(std::string name,
1365 const TMember& (T::*access)() const,
1366 void (T::*mutate)(TMember)
1367 )
1368 {
1369 return member<TMember>(std::move(name),
1370 std::function<const TMember& (const T&)>(access),
1371 [mutate] (T& x, TMember val) { (x.*mutate)(std::move(val)); }
1372 );
1373 }
1374
1375 template <typename TMember>
1376 member_adapter_builder<T, TMember> member(std::string name,
1377 const TMember& (T::*access)() const,
1378 void (T::*mutate)(TMember&&)
1379 )
1380 {
1381 return member<TMember>(std::move(name),
1382 std::function<const TMember& (const T&)>(access),
1383 std::function<void (T&, TMember&&)>(mutate)
1384 );
1385 }
1386
1387 adapter_builder<T>& pre_deserialize(pre_deserialize_func perform)
1388 {
1389 if (_adapter->_pre_deserialize)
1390 {
1391 pre_deserialize_func old_perform = std::move(_adapter->_pre_deserialize);
1392 _adapter->_pre_deserialize = [old_perform, perform] (deserialization_context& context)
1393 {
1394 old_perform(context);
1395 perform(context);
1396 };
1397 }
1398 else
1399 {
1400 _adapter->_pre_deserialize = std::move(perform);
1401 }
1402 return *this;
1403 }
1404
1405 adapter_builder<T>& post_deserialize(post_deserialize_func perform)
1406 {
1407 if (_adapter->_post_deserialize)
1408 {
1409 post_deserialize_func old_perform = std::move(_adapter->_post_deserialize);
1410 _adapter->_post_deserialize = [old_perform, perform] (deserialization_context& context, T&& out)
1411 {
1412 return perform(context, old_perform(context, std::move(out)));
1413 };
1414 }
1415 else
1416 {
1417 _adapter->_post_deserialize = std::move(perform);
1418 }
1419 return *this;
1420 }
1421
1422 /// The handler is stored rather than desugared into a \c pre_deserialize, because the keys which claimed no member
1423 /// are only known once the walk is done. It is still registered against the members as they stand at
1424 /// deserialization time rather than at build time -- the key loop does the matching -- so declaring it before the
1425 /// members it validates against keeps working.
1426 adapter_builder<T>& on_unknown_members(unknown_members_func handler)
1427 {
1428 if (_adapter->_unknown_members)
1429 {
1430 unknown_members_func old_handler = std::move(_adapter->_unknown_members);
1431 _adapter->_unknown_members = [old_handler, handler]
1432 (deserialization_context& context, std::set<std::string> keys)
1433 {
1434 old_handler(context, keys);
1435 handler(context, std::move(keys));
1436 };
1437 }
1438 else
1439 {
1440 _adapter->_unknown_members = std::move(handler);
1441 }
1442 return *this;
1443 }
1444
1445private:
1446 /// Record \a ptr as the type's next member. Every \c member overload comes through here.
1447 ///
1448 /// A name already declared is refused before the \c member_adapter_builder exists, so a refused member leaves no
1449 /// \c reference_type behind for \c check_references to go looking for. Only the name a member is declared with is
1450 /// checked: it is the key the member is written under, and a second member sharing it used to be silently dropped
1451 /// when the members were collected into a \c value. An \c alias is never written, and a document offering a name
1452 /// more than one member answers to is settled by declaration order, as the reference says.
1453 template <typename TMember>
1454 member_adapter_builder<T, TMember> add_member(std::unique_ptr<detail::member_adapter_impl<T, TMember>> ptr)
1455 {
1456 for (const auto& existing : _adapter->_members)
1457 if (existing->primary_name() == ptr->primary_name())
1458 throw std::invalid_argument("Member \"" + std::string(ptr->primary_name())
1459 + "\" is already declared for " + demangle(typeid(T).name())
1460 );
1461
1462 member_adapter_builder<T, TMember> builder(formats_builder_dsl::owner, this, ptr.get());
1463 _adapter->_members.emplace_back(std::move(ptr));
1464 return builder;
1465 }
1466
1467private:
1468 class adapter_impl :
1469 public adapter_for<T>
1470 {
1471 public:
1472 adapter_impl() :
1473 _default_on_null(false)
1474 { }
1475
1477 virtual std::expected<T, ast_node_type> create(deserialization_context& context, reader& from) const override
1478 {
1479 // Made before anything else runs, so that from here on this is the value `source_value` answers for. Its
1480 // text waits for the walk to reach the `}`, which is the first point anything knows where it ends.
1481 detail::source_scope source(context, from);
1482
1483 if (_pre_deserialize)
1484 _pre_deserialize(context);
1485
1486 // As for a member: without a default to take, a `null` is refused below as the non-object it is.
1487 if (_default_on_null && _create_default && detail::current_is_null(from))
1488 {
1489 // The default stands in for this type rather than being about the `null` it replaces, and the scope
1490 // cannot show what the cursor is about to step past.
1491 source.hide();
1492 (void) from.next_token();
1493
1494 try
1495 {
1496 return _create_default(context);
1497 }
1498 catch (...)
1499 {
1500 // The `null` this stands in for is already behind the cursor, so whatever recovers from this
1501 // must not step over the value after it as well. Both the factory and the move of its result
1502 // into the answer are the caller's code.
1503 context.note_value_consumed(from);
1504 throw;
1505 }
1506 }
1507
1508 auto opened = context.current_as<ast_node::object_begin>(from);
1509 if (!opened)
1510 return std::unexpected(opened.error());
1511
1512 source.open(from, *opened);
1513
1514 T out;
1515 detail::member_claim_set claims(_members.size());
1516 bool recovered = false;
1517 bool closed = false;
1518
1519 // Both of these stay empty on the ordinary path and are built only where they are actually wanted. A
1520 // default-constructed container is not free everywhere -- some standard libraries allocate a debugging
1521 // proxy or an end sentinel for one -- and this runs once per object deserialized.
1522 std::optional<std::set<std::string>> unknown_members;
1523 std::optional<std::set<std::string>> repeated_keys;
1524
1525 // `duplicate_key_action::exception` is the one policy which needs to know the keys themselves rather
1526 // than which member they claimed. A member's winning name cannot tell a lower-ranked name it is skipping
1527 // from one it has already skipped, so which of the two an object is refused for would otherwise depend
1528 // on the order it happened to list them in.
1529 if (context.options().on_duplicate_key() == deserialize_options::duplicate_key_action::exception)
1530 repeated_keys.emplace();
1531
1532 // Step off the `{` and onto the first key, or onto the `}` of an empty object. The loop never advances
1533 // itself: deserializing a member leaves the cursor one past its value, which is what every deserializer
1534 // owes its caller.
1535 (void) from.next_token();
1536
1537 try
1538 {
1539 while (from.good())
1540 {
1541 auto type = from.current_type();
1542
1543 if (type == ast_node_type::document_end || type == ast_node_type::error)
1544 {
1545 // A parse which failed part-way through an object still hands back a usable tape; it just
1546 // ends where the rest of the members should have been.
1547 return context.problem(context.problem_path(from), "Unterminated object");
1548 }
1549 else if (type == ast_node_type::object_end)
1550 {
1551 // Deliberately *not* stepped over. Everything after this loop runs user code, and while the
1552 // cursor is on a closing token the reader still names this object rather than the sibling
1553 // after it -- which is both where a failure out of that code belongs and where a caller
1554 // recovering from it resumes, since `reader::next_value` on a `}` is a single step past it.
1555 // It is also the first point at which the whole object can be quoted.
1556 source.close(from);
1557 closed = true;
1558 break;
1559 }
1560
1561 // A canonical key is a view of the source, which outlives this whole deserialization. An escaped
1562 // one has to be decoded, and it is decoded here rather than inside the member because dispatching
1563 // it needs the text anyway; `decoded` owns it for as long as a problem naming it can be raised.
1564 std::optional<std::string> decoded;
1565 std::string_view key;
1566 if (type == ast_node_type::key_canonical)
1567 {
1568 key = from.current().as<ast_node::key_canonical>().value();
1569 }
1570 else if (type == ast_node_type::key_escaped)
1571 {
1572 key = decoded.emplace(from.current().as<ast_node::key_escaped>().value());
1573 }
1574 else
1575 {
1576 auto matched = context.expect(from,
1577 { ast_node_type::key_canonical, ast_node_type::key_escaped }
1578 );
1579 return std::unexpected(matched.error());
1580 }
1581
1582 if (!from.next_token())
1583 return context.problem(context.problem_path(from), "Unterminated object");
1584
1585 if (repeated_keys && !repeated_keys->emplace(key).second)
1586 {
1587 // Asked of every key rather than only of the ones a member answers to, since a key repeated
1588 // twice is the same thing to this policy whether anything claimed it or not -- and it is
1589 // what `parse_index::extract_tree` refuses for the same document.
1590 std::string message("Duplicate key in object: \"");
1591 message.append(key);
1592 message.append("\"");
1593
1594 (void) context.problem(context.path(), std::move(message));
1595
1596 if (!context.recover())
1597 return std::unexpected(ast_node_type::error);
1598
1599 recovered = true;
1600 (void) from.next_value();
1601 continue;
1602 }
1603
1604 auto matched = find_member(key);
1605 if (matched.index == _members.size())
1606 {
1607 // Nothing claimed it. The whole subtree goes by in constant time on a tape-backed reader,
1608 // and its name is only worth keeping if somebody registered to be told about it.
1609 if (_unknown_members)
1610 {
1611 if (!unknown_members)
1612 unknown_members.emplace();
1613
1614 unknown_members->emplace(key);
1615 }
1616
1617 (void) from.next_value();
1618 continue;
1619 }
1620
1621 if (auto held = claims.claim_rank(matched.index); held != detail::member_claim_set::unclaimed)
1622 {
1623 if (matched.rank > held)
1624 {
1625 // Another of this member's names, but one it already answers to by a better one. That is
1626 // the document naming a member two ways rather than repeating a key, so the duplicate
1627 // policy has nothing to say about it and the value goes by unread -- which is also what
1628 // keeps a `check` on the member from being run against a spelling it did not take.
1629 (void) from.next_value();
1630 continue;
1631 }
1632 else if (matched.rank == held && !consume_duplicate(context, from))
1633 {
1634 // The same name twice, which is what `duplicate_key_action` is about.
1635 continue;
1636 }
1637
1638 // Otherwise a name the member prefers to the one it is being read from, which supersedes it.
1639 }
1640
1641 const auto& member = *_members[matched.index];
1642 if (member.defaults_on_null() && detail::current_is_null(from))
1643 {
1644 // `default_on_null` makes a `null` mean what a missing key means, so its default is taken
1645 // where a missing key's is: once the walk is done, when the default can see the whole object.
1646 // It is still a claim, so a repeat of the key is settled against it like any other -- under
1647 // `replace`, a value after the `null` is deserialized over it.
1648 (void) from.next_token();
1649 claims.claim(matched.index, matched.rank, true);
1650 continue;
1651 }
1652
1653 auto walked = run_member(context,
1654 member,
1655 &from,
1656 [&] { return member.deserialize(context, from, key, out); }
1657 );
1658
1659 // Claimed even when it failed: the key was there, so the pass below has nothing to say about it.
1660 claims.claim(matched.index, matched.rank);
1661
1662 if (!walked)
1663 return std::unexpected(walked.error());
1664 else if (!*walked)
1665 recovered = true;
1666 }
1667
1668 if (!closed)
1669 return context.problem(context.problem_path(from), "Unterminated object");
1670
1671 // Reported before the members which never arrived: that is the order the unknown-members handler used
1672 // to run in when it was a `pre_deserialize` -- ahead of everything the walk itself has to say.
1673 if (_unknown_members && unknown_members && !unknown_members->empty())
1674 _unknown_members(context, *std::move(unknown_members));
1675
1676 for (std::size_t idx = 0U; idx < _members.size(); ++idx)
1677 {
1678 if (!claims.wants_default(idx))
1679 continue;
1680
1681 const auto& member = *_members[idx];
1682 if (!member.has_default())
1683 {
1684 std::string message("Missing required field ");
1685 message.append(member.primary_name());
1686
1687 (void) context.problem(context.path(), std::move(message));
1688 if (!context.recover())
1689 return std::unexpected(ast_node_type::error);
1690
1691 recovered = true;
1692 continue;
1693 }
1694
1695 // The factory reads nothing, so there is no value in front of the cursor for a recovery to step
1696 // over -- hence no reader.
1697 auto applied = run_member(context,
1698 member,
1699 nullptr,
1700 [&] { return member.apply_default(context, out); }
1701 );
1702
1703 if (!applied)
1704 return std::unexpected(applied.error());
1705 else if (!*applied)
1706 recovered = true;
1707 }
1708 }
1709 catch (...)
1710 {
1711 // Something left the walk: a setter, a `check` or a default factory, all of which are the
1712 // caller's code. Unless this object's `}` was reached the cursor is somewhere inside it, a position
1713 // only this adapter can make sense of, so finish the walk before letting the failure out. A loop
1714 // above which recovers resumes at the `}`, exactly as it would from a returned failure.
1715 if (!closed)
1716 walk_to_close(from);
1717
1718 throw;
1719 }
1720
1721 // Collecting gathers diagnostics; it does not make a half-populated `out` worth handing back. Reported
1722 // before `_post_deserialize`, which has no business seeing one.
1723 if (recovered)
1724 return std::unexpected(ast_node_type::error);
1725
1726 if (_post_deserialize)
1727 out = _post_deserialize(context, std::move(out));
1728
1729 // Only now, once nothing left is able to fail with this object in front of the cursor.
1730 (void) from.next_token();
1731
1732 try
1733 {
1734 return out;
1735 }
1736 catch (...)
1737 {
1738 // Moving `out` into the answer is the last thing which can fail and `T` is the caller's type. The
1739 // object is behind the cursor by this point, where every other failure in here leaves it in front.
1740 context.note_value_consumed(from);
1741 throw;
1742 }
1743 }
1744
1745 virtual void serialize(const serialization_context& context, const T& from, writer& to) const override
1746 {
1747 // Each member writes its own key and value where the writer is, so the text lists them in the order they
1748 // were declared. The `value` which `to_json` builds from the same tokens sorts them, as every `value` does.
1749 to.object_begin();
1750 for (const auto& member : _members)
1751 member->serialize(context, from, to);
1752 to.object_end();
1753 }
1754
1755 std::deque<std::unique_ptr<detail::member_adapter<T>>> _members;
1756 pre_deserialize_func _pre_deserialize;
1757 post_deserialize_func _post_deserialize;
1758 unknown_members_func _unknown_members;
1759 std::function<T (deserialization_context&)> _create_default;
1760 bool _default_on_null;
1761
1762 private:
1763 /// Which member \a key claimed, and by which of that member's names.
1764 struct member_match
1765 {
1766 /// The member, or \c _members.size() when the key claimed none.
1767 std::size_t index;
1768
1769 /// Which of its names matched, lower being preferred; \c detail::no_deserialize_key when none did.
1770 std::size_t rank;
1771 };
1772
1773 /// Find the member which claims \a key.
1774 ///
1775 /// A linear scan of the members, each scanning its own names. For the member counts this DSL is used with
1776 /// that is cheaper than anything with a hash in it.
1778 member_match find_member(std::string_view key) const
1779 {
1780 for (std::size_t idx = 0U; idx < _members.size(); ++idx)
1781 if (auto rank = _members[idx]->deserialize_key_rank(key); rank != detail::no_deserialize_key)
1782 return member_match{ idx, rank };
1783
1784 return member_match{ _members.size(), detail::no_deserialize_key };
1785 }
1786
1787 /// Deal with \a key naming a member some earlier key already set, with the cursor on the repeat's value.
1788 ///
1789 /// \returns \c true to go on and deserialize it over the top of what is there, which is
1790 /// \c deserialize_options::duplicate_key_action::replace; \c false when the value has been stepped
1791 /// over and the walk should move on to the next key.
1792 ///
1793 /// \c duplicate_key_action::exception is not decided here. It is answered for every key on the way past,
1794 /// because it is the only policy which cares about a repeat this member would otherwise never be told
1795 /// about -- a second helping of a name it has already passed over for a better one.
1797 static bool consume_duplicate(deserialization_context& context, reader& from)
1798 {
1799 if (context.options().on_duplicate_key() == deserialize_options::duplicate_key_action::ignore)
1800 {
1801 (void) from.next_value();
1802 return false;
1803 }
1804
1805 return true;
1806 }
1807
1808 /// Run one member's part of the walk and fold however it fails into the channel the loop deals in.
1809 ///
1810 /// Deserialization reports failure by returning, but a member reaches the caller's code in three places -- the
1811 /// setter, a \c check and a default factory -- and those report by throwing whatever they like. Giving
1812 /// them the same shape is what keeps which members get attempted from depending on how the first failing one
1813 /// happened to fail.
1814 ///
1815 /// \param from The reader to resume from, or \c nullptr where \a run reads nothing. A member which failed by
1816 /// returning still has the value it rejected in front of the cursor; one which failed by throwing
1817 /// has already stepped over it, and a default factory never had one.
1818 /// \returns \c true when \a run succeeded and \c false when it failed and was recovered from, in which case
1819 /// the walk continues from where the cursor stands; otherwise the failure to report.
1820 template <typename FRun>
1822 static std::expected<bool, ast_node_type> run_member(deserialization_context& context,
1823 const detail::member_adapter<T>& member,
1824 reader* from,
1825 FRun&& run
1826 )
1827 {
1828 try
1829 {
1830 if (auto result = run())
1831 {
1832 return true;
1833 }
1834 else if (!context.recover())
1835 {
1836 return std::unexpected(result.error());
1837 }
1838 else if (from)
1839 {
1840 context.skip_failed_value(*from);
1841 }
1842 }
1843 catch (const deserialization_error& ex)
1844 {
1845 // The next key starts at a known place, so one bad member does not have to hide every problem after
1846 // it.
1847 if (!context.recover(ex))
1848 throw;
1849 }
1850 catch (const std::bad_alloc&)
1851 {
1852 // Recovering means recording, and recording allocates -- as does the `deserialization_error` below,
1853 // whose constructors are `noexcept`, so failing to allocate inside one terminates rather than
1854 // propagating. There is nothing to be gained by trying.
1855 throw;
1856 }
1857 catch (...)
1858 {
1859 // Asked before anything is built, because translating allocates and rethrowing the original
1860 // untouched is what leaves `fail_immediately` reaching the same translation it always did.
1861 if (context.options().failure_mode() != deserialize_options::on_error::collect_all)
1862 throw;
1863
1864 // `member_adapter::deserialize` names the member it failed in through the key the document used; this
1865 // failure happened outside that -- or, for a default factory, with no key to name it by at all --
1866 // and would otherwise be the one problem in the list which does not say where it came from.
1867 // Unwinding completes before a handler body runs, so any scope the member pushed is long gone by the
1868 // time this one does and there is nothing to double up with.
1869 deserialization_context::path_scope scope(context, member.primary_name());
1870 deserialization_error translated(context.path(), std::current_exception());
1871
1872 if (!context.recover(translated))
1873 throw;
1874 }
1875
1876 return false;
1877 }
1878
1879 /// Walk \a from to this object's own closing token, leaving the cursor on it.
1880 ///
1881 /// Stepping over whole member values is what finds it: \c reader::next_structure cannot, because a member
1882 /// which is itself a structure gets left rather than crossed, landing back inside the object being built.
1883 static void walk_to_close(reader& from) noexcept
1884 {
1885 while (from.good())
1886 {
1887 auto type = from.current_type();
1888 if ( type == ast_node_type::object_end
1889 || type == ast_node_type::document_end
1890 || type == ast_node_type::error
1891 )
1892 {
1893 break;
1894 }
1895 else if (type == ast_node_type::key_canonical || type == ast_node_type::key_escaped)
1896 {
1897 // From a key, this is the whole member -- the key and the value under it.
1898 (void) from.next_key();
1899 }
1900 else
1901 {
1902 (void) from.next_value();
1903 }
1904 }
1905 }
1906 };
1907
1908private:
1909 adapter_impl* _adapter;
1910};
1911
1912template <typename TPointer>
1914 public detail::formats_builder_dsl
1915{
1916public:
1917 template <typename F>
1919 std::string discrimination_key,
1920 F&& f
1921 ) :
1922 formats_builder_dsl(owner),
1923 _adapter(nullptr),
1924 _discrimination_key(std::move(discrimination_key))
1925 {
1926 auto adapter = std::make_shared<polymorphic_adapter<TPointer>>();
1927 register_adapter(adapter);
1928 _adapter = adapter.get();
1929
1930 std::forward<F>(f)(*this);
1931 }
1932
1933 explicit polymorphic_adapter_builder(formats_builder* owner, std::string discrimination_key = "") :
1935 std::move(discrimination_key),
1937 )
1938 { }
1939
1940 polymorphic_adapter_builder& check_null_input(bool on = true)
1941 {
1942 _adapter->check_null_input(on);
1943 return *this;
1944 }
1945
1946 polymorphic_adapter_builder& check_null_output(bool on = true)
1947 {
1948 _adapter->check_null_output(on);
1949 return *this;
1950 }
1951
1952 template <typename TSub>
1953 polymorphic_adapter_builder& subtype(value discrimination_value,
1955 {
1956 if (_discrimination_key.empty())
1957 throw std::logic_error("Cannot use single-argument subtype if no discrimination_key has been set");
1958
1959 return subtype<TSub>(_discrimination_key, std::move(discrimination_value), action);
1960 }
1961
1962 template <typename TSub>
1963 polymorphic_adapter_builder& subtype(std::string discrimination_key,
1964 value discrimination_value,
1966 {
1967 _adapter->template add_subtype_keyed<TSub>(std::move(discrimination_key),
1968 std::move(discrimination_value),
1969 action);
1970 reference_type(std::type_index(typeid(TSub)), std::type_index(typeid(TPointer)));
1971 return *this;
1972 }
1973
1974 template <typename TSub>
1975 polymorphic_adapter_builder& subtype(std::function<bool (deserialization_context&, const value&)> discriminator)
1976 {
1977 _adapter->template add_subtype<TSub>(std::move(discriminator));
1978 reference_type(std::type_index(typeid(TSub)), std::type_index(typeid(TPointer)));
1979 return *this;
1980 }
1981
1982 template <typename TSub>
1983 polymorphic_adapter_builder& subtype(std::function<bool (const value&)> discriminator)
1984 {
1985 return subtype<TSub>([discriminator = std::move(discriminator)] (deserialization_context&, const value& val)
1986 {
1987 return discriminator(val);
1988 }
1989 );
1990 }
1991
1992private:
1994 std::string _discrimination_key;
1995};
1996
1998{
1999public:
2001
2002 template <typename T>
2003 adapter_builder<T> type()
2004 {
2005 return adapter_builder<T>(this);
2006 }
2007
2008 template <typename T, typename F>
2009 adapter_builder<T> type(F&& f)
2010 {
2011 return adapter_builder<T>(this, std::forward<F>(f));
2012 }
2013
2014 template <typename TEnum>
2015 formats_builder& enum_type(std::string enum_name,
2016 std::initializer_list<std::pair<TEnum, value>> mapping
2017 )
2018 {
2019 return register_adapter(std::make_shared<enum_adapter<TEnum>>(std::move(enum_name), mapping));
2020 }
2021
2022 template <typename TEnum>
2023 formats_builder& enum_type_icase(std::string enum_name,
2024 std::initializer_list<std::pair<TEnum, value>> mapping
2025 )
2026 {
2027 return register_adapter(std::make_shared<enum_adapter_icase<TEnum>>(std::move(enum_name), mapping));
2028 }
2029
2030 template <typename TPointer>
2032 polymorphic_type(std::string discrimination_key = "")
2033 {
2034 return polymorphic_adapter_builder<TPointer>(this, std::move(discrimination_key));
2035 }
2036
2037 template <typename TPointer, typename F>
2039 polymorphic_type(std::string discrimination_key, F&& f)
2040 {
2041 return polymorphic_adapter_builder<TPointer>(this, std::move(discrimination_key), std::forward<F>(f));
2042 }
2043
2044 template <typename F>
2045 formats_builder& extend(F&& func)
2046 {
2047 std::forward<F>(func)(*this);
2048 return *this;
2049 }
2050
2051 formats_builder& register_adapter(const adapter* p)
2052 {
2053 _formats.register_adapter(p, _duplicate_type_action);
2054 return *this;
2055 }
2056
2057 formats_builder& register_adapter(std::shared_ptr<const adapter> p)
2058 {
2059 _formats.register_adapter(std::move(p), _duplicate_type_action);
2060 return *this;
2061 }
2062
2063 template <typename TOptional>
2064 formats_builder& register_optional()
2065 {
2066 reference_type(std::type_index(typeid(typename TOptional::value_type)), std::type_index(typeid(TOptional)));
2067 std::unique_ptr<optional_adapter<TOptional>> p(new optional_adapter<TOptional>);
2068 _formats.register_adapter(std::move(p), _duplicate_type_action);
2069 return *this;
2070 }
2071
2072 template <typename TContainer>
2073 formats_builder& register_container()
2074 {
2075 reference_type(std::type_index(typeid(typename TContainer::value_type)), std::type_index(typeid(TContainer)));
2076 std::unique_ptr<container_adapter<TContainer>> p(new container_adapter<TContainer>);
2077 _formats.register_adapter(std::move(p), _duplicate_type_action);
2078 return *this;
2079 }
2080
2081 template <typename TWrapper>
2082 formats_builder& register_wrapper()
2083 {
2084 reference_type(std::type_index(typeid(typename TWrapper::value_type)), std::type_index(typeid(TWrapper)));
2085 std::unique_ptr<wrapper_adapter<TWrapper>> p(new wrapper_adapter<TWrapper>);
2086 _formats.register_adapter(std::move(p), _duplicate_type_action);
2087 return *this;
2088 }
2089
2090 template <typename T>
2091 formats_builder& register_containers()
2092 {
2093 return *this;
2094 }
2095
2096 template <typename T, template <class...> class TTContainer, template <class...> class... TTRest>
2097 formats_builder& register_containers()
2098 {
2099 register_container<TTContainer<T>>();
2100 return register_containers<T, TTRest...>();
2101 }
2102
2104 operator formats() const
2105 {
2106 return _formats;
2107 }
2108
2109 formats_builder& reference_type(std::type_index type);
2110 formats_builder& reference_type(std::type_index type, std::type_index from);
2111
2112 /// \{
2113
2114 /// Check that every type referenced by this \c formats_builder has both a \c deserializer and a \c serializer in
2115 /// the
2116 /// \c formats it is generating, combined with the \c formats \a other or \a others where they are given.
2117 ///
2118 /// If \a name is non-empty and this function throws, \a name will be provided in the exception's \c what string.
2119 /// This can be useful if you are running multiple \c check_references calls and you want to name the different
2120 /// checks.
2121 ///
2122 /// \throws std::logic_error if some referenced type is missing a \c deserializer or a \c serializer.
2123 formats_builder& check_references(const formats& other, const std::string& name = "");
2124 formats_builder& check_references(const formats::list& others, const std::string& name = "");
2125 formats_builder& check_references(const std::string& name = "");
2126 /// \}
2127
2128 /// \{
2129
2130 /// Check the references of this builder (see \ref check_references) and compose a \ref formats instance if
2131 /// successful (see \ref formats::compose).
2133 formats compose_checked(formats other, const std::string& name = "");
2135 formats compose_checked(formats::list others, const std::string& name = "");
2136 /// \}
2137
2138 /** Assigns the action to perform when a serializer or deserializer is being registered by this formats_builder and
2139 * there is already a serializer or deserializer for that type.
2140 **/
2142
2143private:
2144 void check_references_impl(const formats& searching, const std::string& name);
2145
2146private:
2147 formats _formats;
2148 duplicate_type_action _duplicate_type_action = duplicate_type_action::exception;
2149 std::map<std::type_index, std::set<std::type_index>> _referenced_types;
2150};
2151
2152namespace detail
2153{
2154
2155template <typename T>
2156adapter_builder<T> formats_builder_dsl::type()
2157{
2158 return owner->type<T>();
2159}
2160
2161template <typename T, typename F>
2162adapter_builder<T> formats_builder_dsl::type(F&& f)
2163{
2164 return owner->type<T>(std::forward<F>(f));
2165}
2166
2167template <typename TEnum>
2168formats_builder& formats_builder_dsl::enum_type(std::string enum_name,
2169 std::initializer_list<std::pair<TEnum, value>> mapping
2170 )
2171{
2172 return owner->enum_type<TEnum>(std::move(enum_name), mapping);
2173}
2174
2175template <typename TEnum>
2176formats_builder& formats_builder_dsl::enum_type_icase(std::string enum_name,
2177 std::initializer_list<std::pair<TEnum, value>> mapping
2178 )
2179{
2180 return owner->enum_type_icase<TEnum>(std::move(enum_name), mapping);
2181}
2182
2183template <typename TPointer>
2184polymorphic_adapter_builder<TPointer>
2185formats_builder_dsl::polymorphic_type(std::string discrimination_key)
2186{
2187 return owner->polymorphic_type<TPointer>(std::move(discrimination_key));
2188}
2189
2190template <typename TPointer, typename F>
2191polymorphic_adapter_builder<TPointer>
2192formats_builder_dsl::polymorphic_type(std::string discrimination_key, F&& f)
2193{
2194 return owner->polymorphic_type<TPointer>(std::move(discrimination_key), std::forward<F>(f));
2195}
2196
2197template <typename F>
2198formats_builder& formats_builder_dsl::extend(F&& f)
2199{
2200 return owner->extend(std::forward<F>(f));
2201}
2202
2203template <typename TOptional>
2204formats_builder& formats_builder_dsl::register_optional()
2205{
2206 return owner->register_optional<TOptional>();
2207}
2208
2209template <typename TContainer>
2210formats_builder& formats_builder_dsl::register_container()
2211{
2212 return owner->register_container<TContainer>();
2213}
2214
2215template <typename T, template <class...> class... TTContainers>
2216formats_builder& formats_builder_dsl::register_containers()
2217{
2218 return owner->register_containers<T, TTContainers...>();
2219}
2220
2221template <typename TWrapper>
2222formats_builder& formats_builder_dsl::register_wrapper()
2223{
2224 return owner->register_wrapper<TWrapper>();
2225}
2226
2227template <typename T>
2228adapter_builder<T>& adapter_builder_dsl<T>::type_default_on_null(bool on)
2229{
2230 return owner->type_default_on_null(on);
2231}
2232
2233template <typename T>
2234adapter_builder<T>& adapter_builder_dsl<T>::type_default_value(std::function<T (deserialization_context& ctx)> create)
2235{
2236 return owner->type_default_value(std::move(create));
2237}
2238
2239template <typename T>
2240adapter_builder<T>& adapter_builder_dsl<T>::type_default_value(const T& value)
2241{
2242 return owner->type_default_value(value);
2243}
2244
2245template <typename T>
2246template <typename TMember>
2247member_adapter_builder<T, TMember> adapter_builder_dsl<T>::member(std::string name, TMember T::*selector)
2248{
2249 return owner->member(std::move(name), selector);
2250}
2251
2252template <typename T>
2253template <typename TMember>
2254member_adapter_builder<T, TMember>
2255adapter_builder_dsl<T>::member(std::string name,
2256 std::function<const TMember& (const T&)> access,
2257 std::function<void (T&, TMember&&)> mutate
2258 )
2259{
2260 return owner->member(std::move(name), std::move(access), std::move(mutate));
2261}
2262
2263template <typename T>
2264template <typename TMember>
2265member_adapter_builder<T, TMember>
2266adapter_builder_dsl<T>::member(std::string name,
2267 const TMember& (T::*access)() const,
2268 TMember& (T::*mutable_access)()
2269 )
2270{
2271 return owner->member(std::move(name), access, mutable_access);
2272}
2273
2274template <typename T>
2275template <typename TMember>
2276member_adapter_builder<T, TMember>
2277adapter_builder_dsl<T>::member(std::string name,
2278 const TMember& (T::*access)() const,
2279 void (T::*mutate)(TMember)
2280 )
2281{
2282 return owner->member(std::move(name), access, mutate);
2283}
2284
2285template <typename T>
2286template <typename TMember>
2287member_adapter_builder<T, TMember>
2288adapter_builder_dsl<T>::member(std::string name,
2289 const TMember& (T::*access)() const,
2290 void (T::*mutate)(TMember&&)
2291 )
2292{
2293 return owner->member(std::move(name), access, mutate);
2294}
2295
2296template <typename T>
2297adapter_builder<T>&
2298adapter_builder_dsl<T>::pre_deserialize(typename adapter_builder<T>::pre_deserialize_func perform)
2299{
2300 return owner->pre_deserialize(std::move(perform));
2301}
2302
2303template <typename T>
2304adapter_builder<T>&
2305adapter_builder_dsl<T>::post_deserialize(typename adapter_builder<T>::post_deserialize_func perform)
2306{
2307 return owner->post_deserialize(std::move(perform));
2308}
2309
2310template <typename T>
2311adapter_builder<T>&
2312adapter_builder_dsl<T>::on_unknown_members(typename adapter_builder<T>::unknown_members_func handler)
2313{
2314 return owner->on_unknown_members(std::move(handler));
2315}
2316
2317}
2318
2319/** Throw a \c deserialization_error naming the \a unknown_members, the keys which claimed no member.
2320 *
2321 * \throws deserialization_error always.
2322**/
2325 const std::set<std::string>& unknown_members
2326 );
2327
2328}
member_adapter_builder< T, TMember > member(std::string name, TMember T::*selector)
member_adapter_builder< T, TMember > member(std::string name, std::function< const TMember &(const T &)> access, std::function< void(T &, TMember &&)> mutate)
adapter_builder< T > & on_unknown_members(unknown_members_func handler)
The handler is stored rather than desugared into a pre_deserialize, because the keys which claimed no...
virtual void serialize(const serialization_context &context, const void *from, writer &to) const override
An adapter is both a deserializer and a serializer.
Definition adapter.hpp:27
An adapter for container types.
Provides extra information to routines used for deserialization and serialization.
Definition context.hpp:26
const std::optional< jsonv::version > & version() const
Get the version this context was created with.
Definition context.hpp:49
Provides extra information to routines used for deserialization, collects the problems they encounter...
@ collect_all
Keep deserializing past a problem wherever something knows how to resume, so the deserialization_erro...
@ exception
Repeated keys should raise a deserialization_error.
An adapter for enumeration types.
formats compose_checked(formats::list others, const std::string &name="")
Check the references of this builder (see check_references) and compose a formats instance if success...
formats compose_checked(formats other, const std::string &name="")
Check the references of this builder (see check_references) and compose a formats instance if success...
formats_builder & check_references(const formats &other, const std::string &name="")
Check that every type referenced by this formats_builder has both a deserializer and a serializer in ...
formats_builder & check_references(const std::string &name="")
Check that every type referenced by this formats_builder has both a deserializer and a serializer in ...
formats_builder & on_duplicate_type(duplicate_type_action action) noexcept
Assigns the action to perform when a serializer or deserializer is being registered by this formats_b...
formats_builder & check_references(const formats::list &others, const std::string &name="")
Check that every type referenced by this formats_builder has both a deserializer and a serializer in ...
Simply put, this class is a collection of deserializer and serializer instances.
Definition formats.hpp:157
member_adapter_builder & default_value(TMember value)
If the key for this member is not in the object when deserializing, use this value.
member_adapter_builder & alias(std::string name)
When deserializing, also look for this name as a key.
member_adapter_builder & default_value(std::function< TMember(deserialization_context &)> create)
If the key for this member is not in the object when deserializing, call this function to create a va...
member_adapter_builder & since(version ver)
Only serialize this member if the serialization_context was not created with a version,...
member_adapter_builder & default_on_null(bool on=true)
Should a kind::null for a key be interpreted as a missing value? Only considered if a default_value w...
member_adapter_builder & after(version ver)
Only serialize this member if the serialization_context was not created with a version,...
member_adapter_builder & serialize_if(std::function< bool(const serialization_context &, const TMember &)> check)
Only serialize this member if the check passes.
member_adapter_builder & before(version ver)
Only serialize this member if the serialization_context was not created with a version,...
member_adapter_builder & until(version ver)
Only serialize this member if the serialization_context was not created with a version,...
member_adapter_builder & check(std::function< bool(const TMember &)> accept, TException ex)
Constrained to a TException which cannot be called with the value as a non-const lvalue,...
An adapter for optional-like types.
An adapter which can create polymorphic types.
Provides extra information to routines used for serialization: the formats to find other serializers ...
Definition serialize.hpp:42
Represents a single JSON value, which can be any one of a potential kind, each behaving slightly diff...
Definition value.hpp:113
An adapter for "wrapper" types.
Copyright (c) 2014-2020 by Travis Gockel.
Copyright (c) 2015-2026 by Travis Gockel.
Copyright (c) 2015 by Travis Gockel.
Copyright (c) 2015-2026 by Travis Gockel.
#define JSONV_NO_RETURN
Mark that a given function will never return control to the caller, either by exiting or throwing an ...
Definition config.hpp:150
#define JSONV_NODISCARD
Warn if the caller discards the result of this function.
Definition config.hpp:143
#define JSONV_PUBLIC
This function or class is part of the public API for JSON Voorhees.
Definition config.hpp:124
JSONV_PUBLIC std::string demangle(std::string_view source)
Convert the input source from a mangled type into a human-friendly version.
duplicate_type_action
The action to take when an insertion of a deserializer or serializer into a formats is attempted,...
Definition formats.hpp:35
std::string serialize(const T &from, const formats &fmts=formats::global())
Serialize from into JSON text using fmts (by default jsonv::formats::global()).
keyed_subtype_action
What to do when serializing a keyed subtype of a polymorphic_adapter.
T deserialize(const value &from, const formats &fmts)
Deserialize a C++ value from from using the provided fmts.
@ 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.
STL namespace.
Copyright (c) 2017-2026 by Travis Gockel.
Copyright (c) 2017-2026 by Travis Gockel.
Conversion between C++ types and JSON values.
JSONV_NO_RETURN JSONV_PUBLIC void deny_unknown_members(deserialization_context &context, const std::set< std::string > &unknown_members)
Throw a deserialization_error naming the unknown_members, the keys which claimed no member.
Represents a version used to deserialize and encode JSON objects from C++ classes.
Definition version.hpp:24
Copyright (c) 2015-2026 by Travis Gockel.