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