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>
16#include <jsonv/serialization/container_adapter.hpp>
21
22#include <deque>
23#include <map>
24#include <memory>
25#include <set>
26#include <type_traits>
27
28namespace jsonv
29{
30
31/// \page serialization_builder_dsl Serialization Builder DSL
32///
33/// Most applications tend to have a lot of structure types. While it is possible to write an \c extractor and
34/// \c serializer (or \c adapter) for each type, this can get a little bit tedious. Beyond that, it is very difficult to
35/// look at the contents of adapter code and discover what the JSON might actually look like. The builder DSL is meant
36/// to solve these issues by providing a convenient way to describe conversion operations for your C++ types.
37///
38/// At the end of the day, the goal is to take some C++ structures like this:
39///
40/// \code
41/// struct person
42/// {
43/// std::string first_name;
44/// std::string last_name;
45/// int age;
46/// std::string role;
47/// };
48///
49/// struct company
50/// {
51/// std::string name;
52/// bool certified;
53/// std::vector<person> employees;
54/// std::list<person> candidates;
55/// };
56/// \endcode
57///
58/// ...and easily convert it to an from a JSON representation that looks like this:
59///
60/// \code
61/// {
62/// "name": "Paul's Construction",
63/// "certified": false,
64/// "employees": [
65/// {
66/// "first_name": "Bob",
67/// "last_name": "Builder",
68/// "age": 29
69/// },
70/// {
71/// "first_name": "James",
72/// "last_name": "Johnson",
73/// "age": 38,
74/// "role": "Foreman"
75/// }
76/// ],
77/// "candidates": [
78/// {
79/// "firstname": "Adam",
80/// "lastname": "Ant"
81/// }
82/// ]
83/// }
84/// \endcode
85///
86/// To define a \c formats for this \c person type using the serialization builder DSL, you would say:
87///
88/// \code
89/// jsonv::formats fmts =
90/// jsonv::formats_builder()
91/// .type<person>()
92/// .member("first_name", &person::first_name)
93/// .alternate_name("firstname")
94/// .member("last_name", &person::last_name)
95/// .alternate_name("lastname")
96/// .member("age", &person::age)
97/// .until({ 6,1 })
98/// .default_value(21)
99/// .default_on_null()
100/// .check_input([] (int value) { if (value < 0) throw std::logic_error("Age must be positive."); })
101/// .member("role", &person::role)
102/// .since({ 2,0 })
103/// .default_value("Builder")
104/// .type<company>()
105/// .member("name", &company::name)
106/// .member("certified", &company::certified)
107/// .member("employees", &company::employees)
108/// .member("candidates", &company::candidates)
109/// .register_containers<company, std::vector, std::list>()
110/// .check_references(jsonv::formats::defaults())
111/// ;
112/// \endcode
113///
114/// \section Reference
115///
116/// The DSL is made up of three major parts:
117///
118/// 1. \e formats -- modifies a \c jsonv::formats object by adding new type adapters to it
119/// 2. \e type -- modifies the behavior of a \c jsonv::adapter by adding new members to it
120/// 3. \e member -- modifies an individual member inside of a specific type
121///
122/// Each successive function call transforms your context. \e Narrowing calls make your context more specific; for
123/// example, calling \c type from a \e formats context allows you to modify a specific type. \e Widening calls make the
124/// context less specific and are always available; for example, when in the \e member context, you can still call
125/// \c type from the \e formats context to specify a new type.
126///
127/// \dot
128/// digraph serialization_builder_dsl {
129/// formats [label="formats"]
130/// type [label="type"]
131/// member [label="member"]
132///
133/// formats -> formats
134/// formats -> type
135/// type -> formats
136/// type -> type
137/// type -> member
138/// member -> formats
139/// member -> type
140/// member -> member
141/// }
142/// \enddot
143///
144/// \subsection serialization_builder_dsl_ref_formats Formats Context
145///
146/// Commands in this section modify the behavior of the underlying \c jsonv::formats object.
147///
148/// \subsubsection serialization_builder_dsl_ref_formats_level Level
149///
150/// \paragraph serialization_builder_dsl_ref_formats_level_check_references check_references
151///
152/// - <tt>check_references(formats)</tt>
153/// - <tt>check_references(formats, std::string name)</tt>
154/// - <tt>check_references(formats::list)</tt>
155/// - <tt>check_references(formats::list, std::string name)</tt>
156/// - <tt>check_references()</tt>
157/// - <tt>check_references(std::string name)</tt>
158///
159/// Tests that every type referenced by the members of the output of the DSL have an \c extractor and a \c serializer.
160/// The provided \c formats is used to draw extra types from (a common value is \c jsonv::formats::defaults). In other
161/// words, it asks the question: If the \c formats from this DSL was combined with these other \c formats, could all of
162/// the types be encoded and decoded?
163///
164/// This does not mutate the DSL in any way. On successful verification, it will appear that nothing happened. If the
165/// verification is not successful, an exception will be thrown with the offending types in the message. For example:
166///
167/// \code
168/// There are 2 types referenced that the formats do not know how to serialize:
169/// - date_type (referenced by: name_space::foo, other::name::space::bar)
170/// - tree
171/// \endcode
172///
173/// If \a name is provided, the value will be output to the error message on failure. This can be useful if you have
174/// multiple \c check_references statements and wish to more easily determine the failing \c formats combination from
175/// the error message alone.
176///
177/// \note
178/// This is evaluated \e immediately, so it is best to call this function as the very last step in the DSL.
179///
180/// \code
181/// .check_references(jsonv::formats::defaults())
182/// \endcode
183///
184/// \paragraph serialization_builder_dsl_ref_formats_level_reference_type reference_type
185///
186/// - <tt>reference_type(std::type_index type)</tt>
187/// - <tt>reference_type(std::type_index type, std::type_index from)</tt>
188///
189/// Explicitly add a reference to the provided \a type in the DSL. If \a from is provided, also add a back reference for
190/// tracking purposes. The \a from field is useful for tracking \e why the \a type is referenced.
191///
192/// Type references are used in \ref serialization_builder_dsl_ref_formats_level_check_references to both check and
193/// generate error messages if the \c formats the DSL is building cannot fully create and extract JSON values. You do
194/// not usually have to call this, as each call to \ref serialization_builder_dsl_ref_type_narrowing_member calls this
195/// automatically.
196///
197/// \code
198/// .reference_type(std::type_index(typeid(int)), std::type_index(typeid(my_type)))
199/// .reference_type(std::type_index(typeid(my_type))
200/// \endcode
201///
202/// \paragraph serialization_builder_dsl_ref_formats_level_register_adapter register_adapter
203///
204/// - <tt>register_adapter(const adapter*)</tt>
205/// - <tt>register_adapter(std::shared_ptr&lt;const adapter&gt;)</tt>
206///
207/// Register an arbitrary \c adapter with the \c formats we are currently building. This is useful for integrating with
208/// type adapters that do not (or can not) use the DSL.
209///
210/// \code
211/// .register_adapter(my_type::get_adapter())
212/// \endcode
213///
214/// \paragraph serialization_builder_dsl_ref_formats_level_register_optional register_optional
215///
216/// - <tt>register_optional&lt;TOptional&gt;()</tt>
217///
218/// Similar to \c register_adapter, but automatically create an <tt>optional_adapter&lt;TOptional&gt;</tt> to store.
219///
220/// \code
221/// .register_optional<std::optional<int>>()
222/// .register_optional<boost::optional<double>>()
223/// \endcode
224///
225/// \paragraph serialization_builder_dsl_ref_formats_level_register_container register_container
226///
227/// - <tt>register_container&lt;TContainer&gt;()</tt>
228///
229/// Similar to \c register_adapter, but automatically create a <tt>container_adapter&lt;TContainer&gt;</tt> to store.
230///
231/// \code
232/// .register_container<std::vector<int>>()
233/// .register_container<std::list<std::string>>()
234/// \endcode
235///
236/// \paragraph serialization_builder_dsl_ref_formats_level_register_containers register_containers
237///
238/// - <tt>register_containers&lt;T, template &lt;class...&gt; class... TTContainer&gt;</tt>
239///
240/// Convenience function for calling \c register_container for multiple containers with the same \c value_type.
241/// Unfortunately, it only supports varying the first template parameter of the \c TTContainer types, so if you wish to
242/// do something like vary the allocator, you will have to either call \c register_container multiple times or use a
243/// template alias.
244///
245/// \code
246/// .register_containers<int, std::list, std::deque>()
247/// .register_containers<double, std::vector, std::set>()
248/// \endcode
249///
250/// \paragraph serialization_builder_dsl_ref_formats_level_register_wrapper register_wrapper
251///
252/// - <tt>register_wrapper&lt;TWrapper&gt;()</tt>
253///
254/// Similar to \c register_adapter, but automatically create an <tt>wrapper_adapter&lt;TWrapper&gt;</tt> to store.
255///
256/// \code
257/// .register_optional<std::optional<int>>()
258/// .register_optional<boost::optional<double>>()
259/// \endcode
260///
261/// \paragraph serialization_builder_dsl_ref_formats_level_enum_type enum_type
262///
263/// - <tt>enum_type&lt;TEnum&gt;(std::string name, std::initializer_list&lt;std::pair&lt;TEnum, jsonv::value&gt;&gt;)</tt>
264/// - <tt>enum_type_icase&lt;TEnum&gt;(std::string name, std::initializer_list&lt;std::pair&lt;TEnum, jsonv::value&gt;&gt;)</tt>
265///
266/// Create an adapter for the \c TEnum type with a mapping of C++ values to JSON values and vice versa. The most common
267/// use of this is to map \c enum values in C++ to string representations in JSON. \c TEnum is not restricted to types
268/// which are \c enum, but can be anything which you would like to restrict to a limited subset of possible values.
269/// Likewise, JSON representations are not restricted to being of \c kind::string.
270///
271/// The sibling function \c enum_type_icase will create an adapter which uses case-insensitive checking when converting
272/// to C++ values in \c extract.
273///
274/// \code
275/// .enum_type<ring>("ring",
276/// {
277/// { ring::fire, "fire" },
278/// { ring::wind, "wind" },
279/// { ring::earth, "earth" },
280/// { ring::water, "water" },
281/// { ring::heart, "heart" }, // "heart" is preferred for to_json
282/// { ring::heart, "useless" }, // "useless" is interpreted as ring::heart in extract
283/// { ring::fire, 1 }, // the JSON value 1 will also be interpreted as ring::fire in extract
284/// { ring::ussr, "wind" }, // old C++ value ring::ussr will get output as "wind"
285/// }
286/// )
287/// .enum_type_icase<int>("integer",
288/// {
289/// { 0, "zero" },
290/// { 0, "naught" },
291/// { 1, "one" },
292/// { 2, "two" },
293/// { 3, "three" },
294/// }
295/// )
296/// \endcode
297///
298/// \see enum_adapter
299///
300/// \paragraph serialization_builder_dls_ref_formats_level_polymorphic_type polymorphic_type
301///
302/// - <tt>polymorphic_type<&lt;TPointer&gt;(std::string discrimination_key);</tt>
303///
304/// Create an adapter for the \c TPointer type (usually \c std::shared_ptr or \c std::unique_ptr) that knows how to
305/// serialize and deserialize one or more types that can be polymorphically represented by \c TPointer, i.e. derived
306/// types. It uses a discrimination key to determine which concrete type should be instantiated when extracting values
307/// from json.
308///
309/// \code
310/// .polymorphic_type<std::unique_ptr<base>>("type")
311/// .subtype<derived_1>("derived_1")
312/// .subtype<derived_2>("derived_2", keyed_subtype_action::check)
313/// .subtype<derived_3>("derived_3", keyed_subtype_action::insert);
314/// \endcode
315///
316/// The \ref keyed_subtype_action can be used to configure the adapter to make sure that the discrimination key was
317/// correctly serialized (\ref keyed_subtype_action::check) or to insert the discrimination key for the underlying type
318/// so that the underlying type doesn't need to do that itself (\ref keyed_subtype_action::insert). The default is to do
319/// nothing (\ref keyed_subtype_action::none).
320///
321/// \paragraph serialization_builder_dsl_ref_formats_level_extend extend
322///
323/// - <tt>extend(std::function&lt;void (formats_builder&amp;)&gt; func)</tt>
324///
325/// Extend the \c formats_builder with the provided \a func by passing the current builder to it. This provides a more
326/// convenient way to call helper functions.
327///
328/// \code
329/// jsonv::formats_builder builder;
330/// foo(builder);
331/// bar(builder);
332/// baz(builder);
333/// \endcode
334///
335/// This can be done equivalently with:
336/// \code
337/// jsonv::formats_builder()
338/// .extend(foo)
339/// .extend(bar)
340/// .extend(baz)
341/// \endcode
342///
343/// \paragraph serialization_builder_dsl_ref_formats_level_on_duplicate_type on_duplicate_type
344///
345/// - <tt>on_duplicate_type(on_duplicate_type_action action);</tt>
346///
347/// Set what action to take when attempting to register an adapter, but there is already an adapter for that type in the
348/// formats. The default is to throw a \ref duplicate_type_error exception (\ref duplicate_type_action::exception), but
349/// the \c formats_builder can also be configured to ignore the duplicate (\ref duplicate_type_action::ignore), or to
350/// replace the existing adapter with the new one (\ref duplicate_type_action::replace). This is useful when calling
351/// multiple \c extend methods that may add common types to the \c formats_builder.
352///
353/// \subsubsection serialization_builder_dsl_ref_formats_narrowing Narrowing
354///
355/// \paragraph serialization_builder_dsl_ref_formats_narrowing_type type&lt;T&gt;
356///
357/// - <tt>type&lt;T&gt;()</tt>
358/// - <tt>type&lt;T&gt;(std::function&lt;void (adapter_builder&lt;T&gt;&amp;)&gt; func)</tt>
359///
360/// Create an \c adapter for type \c T and begin building the members for it. If \a func is provided, it will be called
361/// with the adapter_builder&lt;T&gt; this call to \c type creates, which can be used for creating common extension
362/// functions.
363///
364/// \code
365/// .type<my_type>()
366/// .member(...)
367/// .
368/// .
369/// .
370/// \endcode
371///
372///
373/// \subsection serialization_builder_dsl_ref_type Type Context
374///
375/// Commands in this section modify the behavior of the \c jsonv::adapter for a particular type.
376///
377/// \subsubsection serialization_builder_dsl_ref_type_level Level
378///
379/// \paragraph serialization_builder_dsl_ref_type_level_pre_extract pre_extract
380///
381/// - <tt>pre_extract(std::function&lt;void (const extraction_context& context, const value& from)&gt; perform)</tt>
382///
383/// Call the given \a perform function during the \c extract operation, but before performing any extraction. This can
384/// be called multiple times -- all functions will be called in the order they are provided.
385///
386/// \paragraph serialization_builder_dsl_ref_type_level_post_extract post_extract
387///
388/// - <tt>post_extract(std::function&lt;T (const extraction_context& context, T&& out)&gt; perform)</tt>
389///
390/// Call the given \a perform function after the \c extract operation. All functions will be called in the order they
391/// are provided. This allows validation methods to be called on the extracted object as part of extraction.
392/// Postprocessing functions are allowed to mutate the extracted object.
393///
394/// \paragraph serialization_builder_dsl_ref_type_level_default_on_null type_default_on_null
395///
396/// - <tt>type_default_on_null()</tt>
397/// - <tt>type_default_on_null(bool on)</tt>
398///
399/// If the JSON value \c null is in the input, should this type take on some default?
400/// This should be used with \ref serialization_builder_dsl_ref_type_level_type_default_value type_default_value.
401///
402/// \paragraph serialization_builder_dsl_ref_type_level_type_default_value
403///
404/// - <tt>type_default_value(T value)</tt>
405/// - <tt>type_default_value(std::function&lt;T (const extraction_context& context)&gt;)</tt>
406///
407/// What value should be used to create the default for this type?
408///
409/// \code
410/// .type<my_type>()
411/// .type_default_on_null()
412/// .type_default_value(my_type("default"))
413/// \endcode
414///
415/// \paragraph serialization_builder_dsl_ref_type_level_on_extract_extra_keys on_extract_extra_keys
416///
417/// - <tt>on_extract_extra_keys(std::function&lt;void (const extraction_context& context,
418/// const value& from,
419/// std::set&lt;std::string&gt; extra_keys)&gt; action
420/// )</tt>
421///
422/// When extracting, perform some \a action if extra keys are provided. By default, extra keys are usually simply
423/// ignored, so this is useful if you wish to throw an exception (or anything you want).
424///
425/// \code
426/// .type<my_type>()
427/// .member("x", &my_type::x)
428/// .member("y", &my_type::y)
429/// .on_extract_extra_keys([] (const extraction_context&, const value&, std::set<std::string> extra_keys)
430/// {
431/// throw extracted_extra_keys("my_type", std::move(extra_keys));
432/// }
433/// )
434/// \endcode
435///
436/// There is a convenience function named \c throw_extra_keys_extraction_error which does this for you.
437///
438/// \code
439/// .type<my_type>()
440/// .member("x", &my_type::x)
441/// .member("y", &my_type::y)
442/// .on_extract_extra_keys(jsonv::throw_extra_keys_extraction_error)
443/// \endcode
444///
445/// \subsubsection serialization_builder_dsl_ref_type_narrowing Narrowing
446///
447/// \paragraph serialization_builder_dsl_ref_type_narrowing_member member
448///
449/// - <tt>member(std::string name, TMember T::*selector)</tt>
450/// - <tt>member(std::string name, const TMember& (*access)(const T&), void (*mutate)(T&, TMember&&))</tt>
451/// - <tt>member(std::string name, const TMember& (T::*access)() const, TMember& (T::*mutable_access)())</tt>
452/// - <tt>member(std::string name, const TMember& (T::*access)() const, void (T::*mutate)(TMember))</tt>
453/// - <tt>member(std::string name, const TMember& (T::*access)() const, void (T::*mutate)(TMember&&))</tt>
454///
455/// Adds a member to the type we are currently building. By default, the member will be serialized with the key of the
456/// given \a name and the extractor will search for the given \a name. If you wish to change properties of this field,
457/// use the \ref serialization_builder_dsl_ref_member.
458///
459/// \code
460/// .type<my_type>()
461/// .member("x", &my_type::x)
462/// .member("y", &my_type::y)
463/// .member("thing", &my_type::get_thing, &my_type::set_thing)
464/// \endcode
465///
466///
467/// \subsection serialization_builder_dsl_ref_member Member Context
468///
469/// Commands in this section modify the behavior of a particular member. Here, \c T refers to the containing type (the
470/// one we are adding a member to) and \c TMember refers to the type of the member we are modifying.
471///
472/// \subsubsection serialization_builder_dsl_ref_member_level Level
473///
474/// \paragraph serialization_builder_dsl_ref_member_level_after after
475///
476/// - <tt>after(version)</tt>
477///
478/// Only serialize this member if the \c serialization_context::version is not \c version::empty and is greater than or
479/// equal to the provided \c version.
480///
481/// \paragraph serialization_builder_dsl_ref_member_level_alternate_name alternate_name
482///
483/// - <tt>alternate_name(std::string name)</tt>
484///
485/// Provide an alternate name to search for when extracting this member. If a user provides values for multiple names,
486/// preference is given to names earlier in the list, starting with the original given name.
487///
488/// \paragraph serialization_builder_dsl_ref_member_level_before before
489///
490/// - <tt>before(version)</tt>
491///
492/// Only serialize this member if the \c serialization_context::version is not \c version::empty and is less than or
493/// equal to the provided \c version.
494///
495/// \paragraph serialization_builder_dsl_ref_member_level_check_input check_input
496///
497/// - <tt>check_input(std::function&lt;void (const TMember&)&gt; check)</tt>
498/// - <tt>check_input(std::function&lt;bool (const TMember&)&gt; check, std::function&lt;void (const TMember&)&gt; thrower)</tt>
499/// - <tt>check_input(std::function&lt;bool (const TMember&)&gt; check, TException ex)</tt>
500///
501/// Checks the extracted value with the given \a check function. In the first form, you are expected to throw inside the
502/// function. In the latter forms, the second parameter will be invoked (in the case of \a thrower) or thrown directly
503/// (in the case of \a ex).
504///
505/// \code
506/// .member("x", &my_type::x)
507/// .check_input([] (int x) { if (x < 0) throw std::logic_error("x must be greater than 0"); })
508/// .check_input([] (int x) { return x < 100; }, [] (int x) { throw exceptions::less_than(100, x); })
509/// .check_input([] (int x) { return x % 2 == 0; }, std::logic_error("x must be divisible by 2"))
510/// \endcode
511///
512/// \paragraph serialization_builder_dsl_ref_member_level_default_value default_value
513///
514/// - <tt>default_value(TMember value)</tt>
515/// - <tt>default_value(std::function&lt;TMember (const extraction_context&, const value&)&gt; create)</tt>
516///
517/// Provide a default value for this member if no key is found when extracting. You can use the function implementation
518/// to synthesize the key however you want.
519///
520/// \code
521/// .member("x", &my_type::x)
522/// .default_value(10)
523/// \endcode
524///
525/// \paragraph serialization_builder_dsl_ref_member_level_default_on_null default_on_null
526///
527/// - <tt>default_on_null()</tt>
528/// - <tt>default_on_null(bool on)</tt>
529///
530/// If the value associated with this key is \c kind::null, should that be treated as the default value? This option is
531/// only considered if a \ref serialization_builder_dsl_ref_member_level_default_value default_value was provided.
532///
533/// \paragraph serialization_builder_dsl_ref_member_level_encode_if encode_if
534///
535/// - <tt>encode_if(std::function&lt;bool (const serialization_context&, const TMember&amp;)&gt; check)</tt>
536///
537/// Only serialize this member if the \a check function returns true.
538///
539/// \paragraph serialization_builder_dsl_ref_member_level_since since
540///
541/// - <tt>since(version)</tt>
542///
543/// Only serialize this member if the \c serialization_context::version is not \c version::empty and is greater than the
544/// provided \c version.
545///
546/// \paragraph serialization_builder_dsl_ref_member_level_until until
547///
548/// - <tt>until(version)</tt>
549///
550/// Only serialize this member if the \c serialization_context::version is not \c version::empty and is less than the
551/// provided \c version.
552
553class formats_builder;
554
555template <typename T> class adapter_builder;
556template <typename T, typename TMember> class member_adapter_builder;
557template <typename TPointer> class polymorphic_adapter_builder;
558
559namespace detail
560{
561
562class formats_builder_dsl
563{
564public:
565 explicit formats_builder_dsl(formats_builder* owner) :
566 owner(owner)
567 { }
568
569 template <typename T>
570 adapter_builder<T> type();
571
572 template <typename T, typename F>
573 adapter_builder<T> type(F&&);
574
575 template <typename TEnum>
576 formats_builder& enum_type(std::string enum_name, std::initializer_list<std::pair<TEnum, value>> mapping);
577
578 template <typename TEnum>
579 formats_builder& enum_type_icase(std::string enum_name, std::initializer_list<std::pair<TEnum, value>> mapping);
580
581 template <typename TPointer>
582 polymorphic_adapter_builder<TPointer> polymorphic_type(std::string discrimination_key = "");
583
584 template <typename TPointer, typename F>
585 polymorphic_adapter_builder<TPointer> polymorphic_type(std::string discrimination_key, F&&);
586
587 template <typename F>
588 formats_builder& extend(F&&);
589
590 formats_builder& register_adapter(const adapter* p);
591 formats_builder& register_adapter(std::shared_ptr<const adapter> p);
592
593 formats_builder& reference_type(std::type_index typ);
594 formats_builder& reference_type(std::type_index type, std::type_index from);
595
596 template <typename TOptional>
597 formats_builder& register_optional();
598
599 template <typename TContainer>
600 formats_builder& register_container();
601
602 template <typename T, template <class...> class... TTContainers>
603 formats_builder& register_containers();
604
605 template <typename TWrapper>
606 formats_builder& register_wrapper();
607
608 formats_builder& check_references(const formats& other, const std::string& name = "");
609 formats_builder& check_references(const formats::list& others, const std::string& name = "");
610 formats_builder& check_references(const std::string& name = "");
611
612 formats_builder& on_duplicate_type(duplicate_type_action action) noexcept;
613
614 formats compose_checked(formats other, const std::string& name = "");
615 formats compose_checked(std::vector<formats> others, const std::string& name = "");
616
617 operator formats() const;
618
619protected:
620 formats_builder* owner;
621};
622
623template <typename T>
624class adapter_builder_dsl
625{
626public:
627 explicit adapter_builder_dsl(adapter_builder<T>* owner) :
628 owner(owner)
629 { }
630
631 adapter_builder<T>& type_default_on_null(bool on = true);
632
633 adapter_builder<T>& type_default_value(std::function<T (const extraction_context& ctx)> create);
634
635 adapter_builder<T>& type_default_value(const T& value);
636
637 template <typename TMember>
638 member_adapter_builder<T, TMember> member(std::string name, TMember T::*selector);
639
640 template <typename TMember>
641 member_adapter_builder<T, TMember> member(std::string name,
642 std::function<const TMember& (const T&)> access,
643 std::function<void (T&, TMember&&)> mutate
644 );
645
646 template <typename TMember>
647 member_adapter_builder<T, TMember> member(std::string name,
648 const TMember& (T::*access)() const,
649 TMember& (T::*mutable_access)()
650 );
651
652 template <typename TMember>
653 member_adapter_builder<T, TMember> member(std::string name,
654 const TMember& (T::*access)() const,
655 void (T::*mutate)(TMember)
656 );
657
658 template <typename TMember>
659 member_adapter_builder<T, TMember> member(std::string name,
660 const TMember& (T::*access)() const,
661 void (T::*mutate)(TMember&&)
662 );
663
664 adapter_builder<T>& pre_extract(typename adapter_builder<T>::pre_extract_func perform);
665
666 adapter_builder<T>& post_extract(typename adapter_builder<T>::post_extract_func perform);
667
668 adapter_builder<T>& on_extract_extra_keys(typename adapter_builder<T>::extra_keys_func handler);
669
670protected:
671 adapter_builder<T>* owner;
672};
673
674template <typename T>
675class member_adapter
676{
677public:
678 virtual ~member_adapter() noexcept
679 { }
680
681 virtual void mutate(const extraction_context& context, const value& from, T& out) const = 0;
682
683 virtual void to_json(const serialization_context& context, const T& from, value& out) const = 0;
684
685 virtual bool has_extract_key(std::string_view key) const = 0;
686};
687
688template <typename T, typename TMember>
689class member_adapter_impl :
690 public member_adapter<T>
691{
692public:
693 using mutator_type = std::function<void (T&, TMember&&)>;
694 using accessor_type = std::function<const TMember& (const T&)>;
695
696public:
697 explicit member_adapter_impl(std::string name, mutator_type mutator, accessor_type access) :
698 _names({ std::move(name) }),
699 _set_value(std::move(mutator)),
700 _get_value(std::move(access))
701 { }
702
703 explicit member_adapter_impl(std::string name, TMember T::*selector) :
704 member_adapter_impl(std::move(name),
705 [selector] (T& value, TMember&& x) { value.*selector = std::move(x); },
706 [selector] (const T& value) -> const TMember& { return value.*selector; }
707 )
708 { }
709
710 virtual void mutate(const extraction_context& context, const value& from, T& out) const override
711 {
712 value::const_object_iterator iter;
713 for (const auto& name : _names)
714 if ((iter = from.find(name)) != from.end_object())
715 break;
716
717 bool use_default = false;
718 if (iter == from.end_object())
719 {
720 use_default = bool(_default_value);
721 if (!use_default)
722 throw extraction_error(context.path(), std::string("Missing required field ") + _names.at(0));
723 }
724 else if (_default_on_null && iter->second.kind() == kind::null)
725 {
726 use_default = true;
727 }
728
729 if (use_default)
730 _set_value(out, _default_value(context, from));
731 else
732 _set_value(out, context.extract_sub<TMember>(from, iter->first));
733 }
734
735 virtual void to_json(const serialization_context& context, const T& from, value& out) const override
736 {
737 if (should_encode(context, from))
738 out.insert({ _names.at(0), context.to_json(_get_value(from)) });
739 }
740
741 virtual bool has_extract_key(std::string_view key) const override
742 {
743 return std::any_of(begin(_names), end(_names), [key] (const std::string& name) { return name == key; });
744 }
745
746 void add_encode_check(std::function<bool (const serialization_context&, const TMember&)> check)
747 {
748 if (_should_encode)
749 {
750 auto old_check = std::move(_should_encode);
751 _should_encode = [check, old_check] (const serialization_context& context, const TMember& value)
752 {
753 return check(context, value) && old_check(context, value);
754 };
755 }
756 else
757 {
758 _should_encode = std::move(check);
759 }
760 }
761
762 void add_extraction_mutator(std::function <TMember (TMember&&)> mutate)
763 {
764 if (_extract_mutate)
765 {
766 auto old_mutate = std::move(_extract_mutate);
767 _extract_mutate = [old_mutate, mutate] (TMember&& member) { return mutate(old_mutate(std::move(member))); };
768 }
769 else
770 {
771 _extract_mutate = std::move(mutate);
772 }
773 }
774
775 void add_extraction_check(std::function <void (const TMember&)> check)
776 {
777 add_extraction_mutator([check] (TMember&& value)
778 {
779 check(value);
780 return value;
781 });
782 }
783
784 void default_value(std::function<TMember (const extraction_context&, const value&)>&& create)
785 {
786 _default_value = std::move(create);
787 }
788
789 void default_on_null(bool on)
790 {
791 _default_on_null = on;
792 }
793
794private:
795 bool should_encode(const serialization_context& context, const T& from) const
796 {
797 if (_should_encode)
798 return _should_encode(context, _get_value(from));
799 else
800 return true;
801 }
802
803private:
804 template <typename U, typename UMember>
805 friend class member_adapter_builder;
806
807private:
808 std::vector<std::string> _names;
809 mutator_type _set_value;
810 accessor_type _get_value;
811 std::function<bool (const serialization_context&, const TMember&)> _should_encode;
812 std::function<TMember (const extraction_context&, const value&)> _default_value;
813 bool _default_on_null = false;
814 std::function<TMember (TMember&&)> _extract_mutate;
815};
816
817}
818
819template <typename T, typename TMember>
821 public detail::formats_builder_dsl,
822 public detail::adapter_builder_dsl<T>
823{
824public:
827 detail::member_adapter_impl<T, TMember>* adapter
828 ) :
829 formats_builder_dsl(fmt_builder),
830 detail::adapter_builder_dsl<T>(adapt_builder),
831 _adapter(adapter)
832 {
833 reference_type(std::type_index(typeid(TMember)), std::type_index(typeid(T)));
834 }
835
836 /** When extracting, also look for this \a name as a key. **/
838 {
839 _adapter->_names.emplace_back(std::move(name));
840 return *this;
841 }
842
843 member_adapter_builder& check_input(std::function<void (const TMember&)> check)
844 {
845 _adapter->add_extraction_check(std::move(check));
846 return *this;
847 }
848
849 member_adapter_builder& check_input(std::function<bool (const TMember&)> check,
850 std::function<void (const TMember&)> thrower
851 )
852 {
853 _adapter->add_extraction_check([check, thrower] (const TMember& value)
854 {
855 if (!check(value))
856 thrower(value);
857 });
858 return *this;
859 }
860
861 template <typename TException>
862 member_adapter_builder& check_input(std::function<void (const TMember&)> check, const TException& ex)
863 {
864 return check_input(std::move(check), [ex] (const TMember&) { throw ex; });
865 }
866
867 /** If the key for this member is not in the object when deserializing, call this function to create a value. If a
868 * \c default_value is not specified, the key is required.
869 **/
870 member_adapter_builder& default_value(std::function<TMember (const extraction_context&, const value&)> create)
871 {
872 _adapter->default_value(std::move(create));
873 return *this;
874 }
875
876 /** If the key for this member is not in the object when deserializing, use this \a value. If a \c default_value is
877 * not specified, the key is required.
878 **/
883
884 /** Should a \c kind::null for a key be interpreted as a missing value? **/
886 {
887 _adapter->default_on_null(on);
888 return *this;
889 }
890
891 /** Only encode this member if the \a check passes. The final decision to encode is based on \e all \c check
892 * functions.
893 **/
895 {
896 _adapter->add_encode_check(std::move(check));
897 return *this;
898 }
899
900 /** Only encode this member if the \c serialization_context::version is greater than or equal to \a ver. **/
902 {
903 return encode_if([ver] (const serialization_context& context, const TMember&)
904 {
905 return context.version().empty() || context.version() >= ver;
906 }
907 );
908 }
909
910 /** Only encode this member if the \c serialization_context::version is less than or equal to \a ver. **/
912 {
913 return encode_if([ver] (const serialization_context& context, const TMember&)
914 {
915 return context.version().empty() || context.version() <= ver;
916 }
917 );
918 }
919
920 /** Only encode this member if the \c serialization_context::version is greater than \a ver. **/
922 {
923 return encode_if([ver] (const serialization_context& context, const TMember&)
924 {
925 return context.version().empty() || context.version() > ver;
926 }
927 );
928 }
929
930 /** Only encode this member if the \c serialization_context::version is less than \a ver. **/
932 {
933 return encode_if([ver] (const serialization_context& context, const TMember&)
934 {
935 return context.version().empty() || context.version() < ver;
936 }
937 );
938 }
939
940private:
941 detail::member_adapter_impl<T, TMember>* _adapter;
942};
943
944template <typename T>
946 public detail::formats_builder_dsl
947{
948public:
949 using pre_extract_func = std::function<void (const extraction_context&, const value&)>;
950 using post_extract_func = std::function<T (const extraction_context&, T&&)>;
951 using extra_keys_func = std::function<void (const extraction_context&, const value&, std::set<std::string>)>;
952
953public:
954 template <typename F>
955 explicit adapter_builder(formats_builder* owner, F&& f) :
956 formats_builder_dsl(owner),
957 _adapter(nullptr)
958 {
959 auto adapter = std::make_shared<adapter_impl>();
960 register_adapter(adapter);
961 _adapter = adapter.get();
962
963 std::forward<F>(f)(*this);
964 }
965
966 explicit adapter_builder(formats_builder* owner) :
967 adapter_builder(owner, [] (const adapter_builder<T>&) { })
968 { }
969
970 adapter_builder<T>& type_default_on_null(bool on = true)
971 {
972 _adapter->_default_on_null = on;
973 return *this;
974 }
975
976 adapter_builder<T>& type_default_value(std::function<T (const extraction_context& ctx)> create)
977 {
978 _adapter->_create_default = std::move(create);
979 return *this;
980 }
981
982 adapter_builder<T>& type_default_value(const T& value)
983 {
984 return type_default_value([value] (const extraction_context&) { return T(value); });
985 }
986
987 template <typename TMember>
989 {
990 std::unique_ptr<detail::member_adapter_impl<T, TMember>> ptr
991 (
992 new detail::member_adapter_impl<T, TMember>(std::move(name), selector)
993 );
994 member_adapter_builder<T, TMember> builder(formats_builder_dsl::owner, this, ptr.get());
995 _adapter->_members.emplace_back(std::move(ptr));
996 return builder;
997 }
998
999 template <typename TMember>
1000 member_adapter_builder<T, TMember> member(std::string name,
1001 std::function<const TMember& (const T&)> access,
1002 std::function<void (T&, TMember&&)> mutate
1003 )
1004 {
1005 std::unique_ptr<detail::member_adapter_impl<T, TMember>> ptr
1006 (
1007 new detail::member_adapter_impl<T, TMember>(std::move(name), std::move(mutate), std::move(access))
1008 );
1009 member_adapter_builder<T, TMember> builder(formats_builder_dsl::owner, this, ptr.get());
1010 _adapter->_members.emplace_back(std::move(ptr));
1011 return builder;
1012 }
1013
1014 template <typename TMember>
1015 member_adapter_builder<T, TMember> member(std::string name,
1016 const TMember& (T::*access)() const,
1017 TMember& (T::*mutable_access)()
1018 )
1019 {
1020 return member<TMember>(std::move(name),
1021 access,
1022 [mutable_access] (T& x, TMember&& val) { (x.*mutable_access)() = std::move(val); }
1023 );
1024 }
1025
1026 template <typename TMember>
1027 member_adapter_builder<T, TMember> member(std::string name,
1028 const TMember& (T::*access)() const,
1029 void (T::*mutate)(TMember)
1030 )
1031 {
1032 return member<TMember>(std::move(name),
1033 std::function<const TMember& (const T&)>(access),
1034 [mutate] (T& x, TMember val) { (x.*mutate)(std::move(val)); }
1035 );
1036 }
1037
1038 template <typename TMember>
1039 member_adapter_builder<T, TMember> member(std::string name,
1040 const TMember& (T::*access)() const,
1041 void (T::*mutate)(TMember&&)
1042 )
1043 {
1044 return member<TMember>(std::move(name),
1045 std::function<const TMember& (const T&)>(access),
1046 std::function<void (T&, TMember&&)>(mutate)
1047 );
1048 }
1049
1050 adapter_builder<T>& pre_extract(pre_extract_func perform)
1051 {
1052 if (_adapter->_pre_extract)
1053 {
1054 pre_extract_func old_perform = std::move(_adapter->_pre_extract);
1055 _adapter->_pre_extract = [old_perform, perform] (const extraction_context& context, const value& from)
1056 {
1059 };
1060 }
1061 else
1062 {
1063 _adapter->_pre_extract = std::move(perform);
1064 }
1065 return *this;
1066 }
1067
1068 adapter_builder<T>& post_extract(post_extract_func perform)
1069 {
1070 if (_adapter->_post_extract)
1071 {
1072 post_extract_func old_perform = std::move(_adapter->_post_extract);
1073 _adapter->_post_extract = [old_perform, perform] (const extraction_context& context, T&& out)
1074 {
1075 return perform(context, old_perform(context, std::move(out)));
1076 };
1077 }
1078 else
1079 {
1080 _adapter->_post_extract = std::move(perform);
1081 }
1082 return *this;
1083 }
1084
1085 adapter_builder<T>& on_extract_extra_keys(extra_keys_func handler)
1086 {
1087 adapter_impl* adapter = _adapter;
1088 return pre_extract([adapter, handler] (const extraction_context& context, const value& from)
1089 {
1090 auto is_key = [adapter] (std::string_view key) -> bool
1091 {
1092 return std::any_of(begin(adapter->_members), end(adapter->_members),
1093 [key] (const std::unique_ptr<detail::member_adapter<T>>& mem)
1094 {
1095 return mem->has_extract_key(key);
1096 }
1097 );
1098 };
1099 std::set<std::string> extra_keys;
1100 for (const auto& pair : from.as_object())
1101 if (!is_key(pair.first))
1102 extra_keys.insert(pair.first);
1103 if (!extra_keys.empty())
1104 handler(context, from, std::move(extra_keys));
1105 });
1106 }
1107
1108private:
1109 class adapter_impl :
1110 public adapter_for<T>
1111 {
1112 public:
1113 adapter_impl() :
1114 _default_on_null(false)
1115 { }
1116
1117 virtual T create(const extraction_context& context, const value& from) const override
1118 {
1119 if (_pre_extract)
1120 _pre_extract(context, from);
1121
1122 if (_default_on_null && from.is_null())
1123 return _create_default(context);
1124
1125 T out;
1126 for (const auto& member : _members)
1127 member->mutate(context, from, out);
1128
1129 if (_post_extract)
1130 out = _post_extract(context, std::move(out));
1131
1132 return out;
1133 }
1134
1135 virtual value to_json(const serialization_context& context, const T& from) const override
1136 {
1137 value out = object();
1138 for (const auto& member : _members)
1139 member->to_json(context, from, out);
1140 return out;
1141 }
1142
1143 std::deque<std::unique_ptr<detail::member_adapter<T>>> _members;
1144 pre_extract_func _pre_extract;
1145 post_extract_func _post_extract;
1146 std::function<T (const extraction_context&)> _create_default;
1147 bool _default_on_null;
1148 };
1149
1150private:
1151 adapter_impl* _adapter;
1152};
1153
1154template <typename TPointer>
1156 public detail::formats_builder_dsl
1157{
1158public:
1159 template <typename F>
1161 std::string discrimination_key,
1162 F&& f
1163 ) :
1164 formats_builder_dsl(owner),
1165 _adapter(nullptr),
1166 _discrimination_key(std::move(discrimination_key))
1167 {
1168 auto adapter = std::make_shared<polymorphic_adapter<TPointer>>();
1169 register_adapter(adapter);
1170 _adapter = adapter.get();
1171
1172 std::forward<F>(f)(*this);
1173 }
1174
1175 explicit polymorphic_adapter_builder(formats_builder* owner, std::string discrimination_key = "") :
1177 std::move(discrimination_key),
1179 )
1180 { }
1181
1182 polymorphic_adapter_builder& check_null_input(bool on = true)
1183 {
1184 _adapter->check_null_input(on);
1185 return *this;
1186 }
1187
1188 polymorphic_adapter_builder& check_null_output(bool on = true)
1189 {
1190 _adapter->check_null_output(on);
1191 return *this;
1192 }
1193
1194 template <typename TSub>
1197 {
1198 if (_discrimination_key.empty())
1199 throw std::logic_error("Cannot use single-argument subtype if no discrimination_key has been set");
1200
1201 return subtype<TSub>(_discrimination_key, std::move(discrimination_value), action);
1202 }
1203
1204 template <typename TSub>
1208 {
1209 _adapter->template add_subtype_keyed<TSub>(std::move(discrimination_key),
1210 std::move(discrimination_value),
1211 action);
1212 reference_type(std::type_index(typeid(TSub)), std::type_index(typeid(TPointer)));
1213 return *this;
1214 }
1215
1216 template <typename TSub>
1217 polymorphic_adapter_builder& subtype(std::function<bool (const extraction_context&, const value&)> discriminator)
1218 {
1219 _adapter->template add_subtype<TSub>(std::move(discriminator));
1220 reference_type(std::type_index(typeid(TSub)), std::type_index(typeid(TPointer)));
1221 return *this;
1222 }
1223
1224 template <typename TSub>
1225 polymorphic_adapter_builder& subtype(std::function<bool (const value&)> discriminator)
1226 {
1227 return subtype<TSub>([discriminator] (const extraction_context&, const value& val)
1228 {
1229 return discriminator(val);
1230 }
1231 );
1232 }
1233
1234private:
1236 std::string _discrimination_key;
1237};
1238
1240{
1241public:
1243
1244 template <typename T>
1245 adapter_builder<T> type()
1246 {
1247 return adapter_builder<T>(this);
1248 }
1249
1250 template <typename T, typename F>
1251 adapter_builder<T> type(F&& f)
1252 {
1253 return adapter_builder<T>(this, std::forward<F>(f));
1254 }
1255
1256 template <typename TEnum>
1257 formats_builder& enum_type(std::string enum_name,
1258 std::initializer_list<std::pair<TEnum, value>> mapping
1259 )
1260 {
1261 return register_adapter(std::make_shared<enum_adapter<TEnum>>(std::move(enum_name), mapping));
1262 }
1263
1264 template <typename TEnum>
1265 formats_builder& enum_type_icase(std::string enum_name,
1266 std::initializer_list<std::pair<TEnum, value>> mapping
1267 )
1268 {
1269 return register_adapter(std::make_shared<enum_adapter_icase<TEnum>>(std::move(enum_name), mapping));
1270 }
1271
1272 template <typename TPointer>
1274 polymorphic_type(std::string discrimination_key = "")
1275 {
1277 }
1278
1279 template <typename TPointer, typename F>
1281 polymorphic_type(std::string discrimination_key, F&& f)
1282 {
1283 return polymorphic_adapter_builder<TPointer>(this, std::move(discrimination_key), std::forward<F>(f));
1284 }
1285
1286 template <typename F>
1287 formats_builder& extend(F&& func)
1288 {
1289 std::forward<F>(func)(*this);
1290 return *this;
1291 }
1292
1293 formats_builder& register_adapter(const adapter* p)
1294 {
1295 _formats.register_adapter(p, _duplicate_type_action);
1296 return *this;
1297 }
1298
1299 formats_builder& register_adapter(std::shared_ptr<const adapter> p)
1300 {
1301 _formats.register_adapter(std::move(p), _duplicate_type_action);
1302 return *this;
1303 }
1304
1305 template <typename TOptional>
1306 formats_builder& register_optional()
1307 {
1308 reference_type(std::type_index(typeid(typename TOptional::value_type)), std::type_index(typeid(TOptional)));
1309 std::unique_ptr<optional_adapter<TOptional>> p(new optional_adapter<TOptional>);
1310 _formats.register_adapter(std::move(p), _duplicate_type_action);
1311 return *this;
1312 }
1313
1314 template <typename TContainer>
1315 formats_builder& register_container()
1316 {
1317 reference_type(std::type_index(typeid(typename TContainer::value_type)), std::type_index(typeid(TContainer)));
1318 std::unique_ptr<container_adapter<TContainer>> p(new container_adapter<TContainer>);
1319 _formats.register_adapter(std::move(p), _duplicate_type_action);
1320 return *this;
1321 }
1322
1323 template <typename TWrapper>
1324 formats_builder& register_wrapper()
1325 {
1326 reference_type(std::type_index(typeid(typename TWrapper::value_type)), std::type_index(typeid(TWrapper)));
1327 std::unique_ptr<wrapper_adapter<TWrapper>> p(new wrapper_adapter<TWrapper>);
1328 _formats.register_adapter(std::move(p), _duplicate_type_action);
1329 return *this;
1330 }
1331
1332 template <typename T>
1333 formats_builder& register_containers()
1334 {
1335 return *this;
1336 }
1337
1338 template <typename T, template <class...> class TTContainer, template <class...> class... TTRest>
1339 formats_builder& register_containers()
1340 {
1342 return register_containers<T, TTRest...>();
1343 }
1344
1345 operator formats() const
1346 {
1347 return _formats;
1348 }
1349
1350 formats_builder& reference_type(std::type_index type);
1351 formats_builder& reference_type(std::type_index type, std::type_index from);
1352
1353 /// \{
1354 /// Check that, when combined with the \c formats \a other, all types referenced by this \c formats_builder will
1355 /// get decoded properly.
1356 ///
1357 /// \param name if non-empty and this function throws, this \a name will be provided in the exception's \c what
1358 /// string. This can be useful if you are running multiple \c check_references calls and you want to
1359 /// name the different checks.
1360 ///
1361 /// \throws std::logic_error if \c formats this \c formats_builder is generating, when combined with the provided
1362 /// \a other \c formats, cannot properly serialize all the types.
1363 formats_builder& check_references(const formats& other, const std::string& name = "");
1364 formats_builder& check_references(const formats::list& others, const std::string& name = "");
1365 formats_builder& check_references(const std::string& name = "");
1366 /// \}
1367
1368 /// \{
1369 /// Check the references of this builder (see \ref check_references) and compose a \ref formats instance if
1370 /// successful (see \ref formats::compose).
1371 formats compose_checked(formats other, const std::string& name = "");
1372 formats compose_checked(const formats::list& others, const std::string& name = "");
1373 /// \}
1374
1375 /** Assigns the action to perform when a serializer or extractor is being registered by this formats_builder and
1376 * there is already a serializer or extracter for that type.
1377 **/
1379
1380private:
1381 void check_references_impl(const formats& searching, const std::string& name);
1382
1383private:
1384 formats _formats;
1385 duplicate_type_action _duplicate_type_action = duplicate_type_action::exception;
1386 std::map<std::type_index, std::set<std::type_index>> _referenced_types;
1387};
1388
1389namespace detail
1390{
1391
1392template <typename T>
1393adapter_builder<T> formats_builder_dsl::type()
1394{
1395 return owner->type<T>();
1396}
1397
1398template <typename T, typename F>
1399adapter_builder<T> formats_builder_dsl::type(F&& f)
1400{
1401 return owner->type<T>(std::forward<F>(f));
1402}
1403
1404template <typename TEnum>
1405formats_builder& formats_builder_dsl::enum_type(std::string enum_name,
1406 std::initializer_list<std::pair<TEnum, value>> mapping
1407 )
1408{
1409 return owner->enum_type<TEnum>(std::move(enum_name), mapping);
1410}
1411
1412template <typename TEnum>
1413formats_builder& formats_builder_dsl::enum_type_icase(std::string enum_name,
1414 std::initializer_list<std::pair<TEnum, value>> mapping
1415 )
1416{
1417 return owner->enum_type_icase<TEnum>(std::move(enum_name), mapping);
1418}
1419
1420template <typename TPointer>
1421polymorphic_adapter_builder<TPointer>
1422formats_builder_dsl::polymorphic_type(std::string discrimination_key)
1423{
1424 return owner->polymorphic_type<TPointer>(std::move(discrimination_key));
1425}
1426
1427template <typename TPointer, typename F>
1428polymorphic_adapter_builder<TPointer>
1429formats_builder_dsl::polymorphic_type(std::string discrimination_key, F&& f)
1430{
1431 return owner->polymorphic_type<TPointer>(std::move(discrimination_key), std::forward<F>(f));
1432}
1433
1434template <typename F>
1435formats_builder& formats_builder_dsl::extend(F&& f)
1436{
1437 return owner->extend(std::forward<F>(f));
1438}
1439
1440template <typename TOptional>
1441formats_builder& formats_builder_dsl::register_optional()
1442{
1443 return owner->register_optional<TOptional>();
1444}
1445
1446template <typename TContainer>
1447formats_builder& formats_builder_dsl::register_container()
1448{
1449 return owner->register_container<TContainer>();
1450}
1451
1452template <typename T, template <class...> class... TTContainers>
1453formats_builder& formats_builder_dsl::register_containers()
1454{
1455 return owner->register_containers<T, TTContainers...>();
1456}
1457
1458template <typename TWrapper>
1459formats_builder& formats_builder_dsl::register_wrapper()
1460{
1461 return owner->register_container<TWrapper>();
1462}
1463
1464template <typename T>
1465adapter_builder<T>& adapter_builder_dsl<T>::type_default_on_null(bool on)
1466{
1467 return owner->type_default_on_null(on);
1468}
1469
1470template <typename T>
1471adapter_builder<T>& adapter_builder_dsl<T>::type_default_value(std::function<T (const extraction_context& ctx)> create)
1472{
1473 return owner->type_default_value(std::move(create));
1474}
1475
1476template <typename T>
1477adapter_builder<T>& adapter_builder_dsl<T>::type_default_value(const T& value)
1478{
1479 return owner->type_default_value(value);
1480}
1481
1482template <typename T>
1483template <typename TMember>
1484member_adapter_builder<T, TMember> adapter_builder_dsl<T>::member(std::string name, TMember T::*selector)
1485{
1486 return owner->member(std::move(name), selector);
1487}
1488
1489template <typename T>
1490template <typename TMember>
1491member_adapter_builder<T, TMember>
1492adapter_builder_dsl<T>::member(std::string name,
1493 std::function<const TMember& (const T&)> access,
1494 std::function<void (T&, TMember&&)> mutate
1495 )
1496{
1497 return owner->member(std::move(name), std::move(access), std::move(mutate));
1498}
1499
1500template <typename T>
1501template <typename TMember>
1502member_adapter_builder<T, TMember>
1503adapter_builder_dsl<T>::member(std::string name,
1504 const TMember& (T::*access)() const,
1505 TMember& (T::*mutable_access)()
1506 )
1507{
1508 return owner->member(std::move(name), access, mutable_access);
1509}
1510
1511template <typename T>
1512template <typename TMember>
1513member_adapter_builder<T, TMember>
1514adapter_builder_dsl<T>::member(std::string name,
1515 const TMember& (T::*access)() const,
1516 void (T::*mutate)(TMember)
1517 )
1518{
1519 return owner->member(std::move(name), access, mutate);
1520}
1521
1522template <typename T>
1523template <typename TMember>
1524member_adapter_builder<T, TMember>
1525adapter_builder_dsl<T>::member(std::string name,
1526 const TMember& (T::*access)() const,
1527 void (T::*mutate)(TMember&&)
1528 )
1529{
1530 return owner->member(std::move(name), access, mutate);
1531}
1532
1533template <typename T>
1534adapter_builder<T>& adapter_builder_dsl<T>::pre_extract(typename adapter_builder<T>::pre_extract_func perform)
1535{
1536 return owner->pre_extract(std::move(perform));
1537}
1538
1539template <typename T>
1540adapter_builder<T>& adapter_builder_dsl<T>::post_extract(typename adapter_builder<T>::post_extract_func perform)
1541{
1542 return owner->post_extract(std::move(perform));
1543}
1544
1545template <typename T>
1546adapter_builder<T>& adapter_builder_dsl<T>::on_extract_extra_keys(typename adapter_builder<T>::extra_keys_func handler)
1547{
1548 return owner->on_extract_extra_keys(std::move(handler));
1549}
1550
1551}
1552
1553/** Throw an \a extraction_error indicating that \a from had extra keys.
1554 *
1555 * \throws extraction_error always.
1556**/
1559 const value& from,
1560 const std::set<std::string>& extra_keys
1561 );
1562
1563}
An adapter for the type T.
An adapter is both an extractor and a serializer.
An adapter for enumeration types.
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.
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::version is greater than or equal to ver.
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 & after(version ver)
Only encode this member if the serialization_context::version is greater than ver.
member_adapter_builder & before(version ver)
Only encode this member if the serialization_context::version is less than ver.
member_adapter_builder & default_value(std::function< TMember(const extraction_context &, const value &)> create)
If the key for this member is not in the object when deserializing, call this function to create a va...
member_adapter_builder & until(version ver)
Only encode this member if the serialization_context::version is less than or equal to ver.
Represents a single JSON value, which can be any one of a potential kind, each behaving slightly diff...
Definition value.hpp:107
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:116
#define JSONV_PUBLIC
This function or class is part of the public API for JSON Voorhees.
Definition config.hpp:102
Copyright (c) 2015 by Travis Gockel.
Copyright (c) 2015-2020 by Travis Gockel.
duplicate_type_action
The action to take when an insertion of an extractor or serializer into a formats is attempted,...
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) 2015-2020 by Travis Gockel.
Copyright (c) 2017-2020 by Travis Gockel.
Conversion between C++ types and JSON values.
JSONV_NO_RETURN JSONV_PUBLIC void throw_extra_keys_extraction_error(const extraction_context &context, const value &from, const std::set< std::string > &extra_keys)
Throw an extraction_error indicating that from had extra keys.
Represents a version used to extract and encode JSON objects from C++ classes.
Definition version.hpp:24
Copyright (c) 2015-2020 by Travis Gockel.