JSON Voorhees
Killer JSON for C++
Loading...
Searching...
No Matches
deserialize.hpp
Go to the documentation of this file.
1/// \file jsonv/serialization/deserialize.hpp
2/// Deserialization of C++ types from a JSON AST.
3///
4/// Copyright (c) 2015-2026 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/ast.hpp>
16#include <jsonv/forward.hpp>
17#include <jsonv/optional.hpp>
18#include <jsonv/parse.hpp>
19#include <jsonv/parse_index.hpp>
20#include <jsonv/path.hpp>
21#include <jsonv/reader.hpp>
23#include <jsonv/value.hpp>
24
25#include <concepts>
26#include <cstddef>
27#include <exception>
28#include <expected>
29#include <functional>
30#include <initializer_list>
31#include <iosfwd>
32#include <memory>
33#include <new>
34#include <optional>
35#include <set>
36#include <string>
37#include <string_view>
38#include <type_traits>
39#include <typeinfo>
40#include <utility>
41#include <variant>
42#include <vector>
43
44namespace jsonv
45{
46
47namespace detail
48{
49
50class borrowed_subtree;
51class source_scope;
52class temporary_source_scope;
53
54/// Does the source a deserialization reads from outlive it?
55enum class source_lifetime : unsigned char
56{
57 /// The caller owns the source and keeps it alive past the deserialization, so what is deserialized may view it.
58 caller,
59 /// The source was handed to the deserialization to own and is freed when it finishes, so nothing deserialized may
60 /// view it.
61 deserialization,
62};
63
64/// The one place a public entry point runs a deserialization: every \c jsonv::deserialize overload and
65/// \c deserialization_context::deserialize(const value&) come through here.
66///
67/// A reader on \c ast_node_type::document_start is deserialized as a whole document. Its source is checked to have
68/// parsed, the \c document_start is stepped over -- here and nowhere else, so a \c deserializer is never entered on one
69/// -- and once the value has been read the reader must be on \c ast_node_type::document_end, where it is left. A
70/// reader the caller has already positioned gets none of that: the value under the cursor is deserialized and the
71/// cursor is left one past it, as \c reader::next_value would. A reader on an \c ast_node_type::error node, positioned
72/// or not, has its source checked as well, since there is no value there and the parse can say why.
73///
74/// \param into Storage for the deserialized object, as for \c deserializer::deserialize.
75/// \param destroy Destroys the object in \a into. A deserializer which left a whole document's reader short of its end
76/// is only found to have done so once the object has been built, and has to be refused after all.
77///
78/// \throws deserialization_error carrying the problems this call recorded, and only those, since a \c value bridge
79/// calls this with a context which may already hold some.
80JSONV_PUBLIC void deserialize_entry(deserialization_context& context,
81 const std::type_info& type,
82 reader& from,
83 void* into,
84 void (* destroy)(void*) noexcept,
85 source_lifetime lifetime
86 );
87
88/// \{
89
90/// Check if \c T is a \c std::expected and, if it is, get the type it holds.
91///
92/// Deserialization functions are allowed to return either a bare \c T or a \c std::expected<T, ast_node_type>, so the
93/// machinery which deduces what an adapter deserializes has to see through the latter.
94template <typename T>
95struct is_expected :
96 std::false_type
97{ };
98
99template <typename T, typename E>
100struct is_expected<std::expected<T, E>> :
101 std::true_type
102{ };
103
104template <typename T>
105inline constexpr bool is_expected_v = is_expected<T>::value;
106
107template <typename T>
108struct expected_value_or_self
109{
110 using type = T;
111};
112
113template <typename T, typename E>
114struct expected_value_or_self<std::expected<T, E>>
115{
116 using type = T;
117};
118
119template <typename T>
120using expected_value_or_self_t = typename expected_value_or_self<T>::type;
121/// \}
122
123}
124
125/// \addtogroup Serialization
126/// \{
127
128/// Exception thrown if there is any problem running \c deserialize.
129///
130/// \c what says where each \c problem was found: at its \c problem::path, and in its \c problem::source_name where it
131/// has one, joined by a \c # as in a URI fragment -- <tt>config.json#.servers[2].port</tt>. A problem with a name and
132/// an empty path is reported in the document as a whole, as <tt>config.json</tt>.
134 public std::runtime_error
135{
136public:
137 /// Description of a single problem with deserialization.
139 {
140 public:
141 /// \{
142
143 /// Create a problem for the given \a path, \a message, and optional \a cause.
144 explicit problem(jsonv::path path, std::string message, std::exception_ptr cause) noexcept;
145 explicit problem(jsonv::path path, std::string message) noexcept;
146 /// \}
147
148 /// Create a problem with a \c message extracted from \a cause.
149 ///
150 /// \param cause The underlying cause of this problem to extract the message from. If the exception backing
151 /// \a cause is not derived from \c std::exception, a message about unknown exception will be used
152 /// instead.
153 explicit problem(jsonv::path path, std::exception_ptr cause) noexcept;
154
155 /// The path this problem was encountered at, within the document \c source_name names.
157 const jsonv::path& path() const noexcept
158 {
159 return _path;
160 }
161
162 /// The name of the document this problem was encountered in, such as the file it was read from. This is empty
163 /// unless the problem was recorded on a \c deserialization_context which was given one, which is where it comes
164 /// from -- see \c deserialization_context::source_name.
166 const std::string& source_name() const noexcept
167 {
168 return _source_name;
169 }
170
171 /// Human-readable details about the encountered problem.
173 const std::string& message() const noexcept
174 {
175 return _message;
176 }
177
178 /// If there was an exception that caused this problem, extra details can be found in the nested exception. This
179 /// can be \c nullptr if there was no underlying cause.
181 const std::exception_ptr& nested_ptr() const noexcept
182 {
183 return _cause;
184 }
185
186 private:
187 /// Names the source of a problem recorded on it.
189
190 private:
191 jsonv::path _path;
192 std::string _message;
193 std::exception_ptr _cause;
194 std::string _source_name;
195 };
196
197 using problem_list = std::vector<problem>;
198
199public:
200 /// Create a \c deserialization_error from the given list of \a problems.
201 ///
202 /// \param problems The list of problems which caused this error. It is expected that \c problems.size() is greater
203 /// than \c 0. If it is not, a single \c problem will be created with a note about an unspecified
204 /// error.
205 explicit deserialization_error(problem_list problems) noexcept;
206
207 /// \{
208
209 /// Create a new \c deserialization_error with a single \c problem from the given \a path, \a message, and optional
210 /// underlying \a cause.
211 explicit deserialization_error(jsonv::path path, std::string message, std::exception_ptr cause) noexcept;
212 explicit deserialization_error(jsonv::path path, std::string message) noexcept;
213 /// \}
214
215 /// Create a new \c deserialization_error with a single \c problem at \a path, whose message is extracted from \a
216 /// cause (see \c problem::problem).
217 explicit deserialization_error(jsonv::path path, std::exception_ptr cause) noexcept;
218
219 virtual ~deserialization_error() noexcept;
220
221 /// Get the path the first deserialization error came from.
223 const jsonv::path& path() const noexcept;
224
225 /// Get the name of the document the first deserialization error came from (see \c problem::source_name). This is
226 /// empty if it was not given one.
228 const std::string& source_name() const noexcept;
229
230 /// Get the first \c problem::cause. This can be \c nullptr if the first \c problem does not have an underlying
231 /// cause.
233 const std::exception_ptr& nested_ptr() const noexcept;
234
235 /// Get the list of problems which caused this \c deserialization_error. There will always be at least one \c
236 /// problem in this list.
238 const problem_list& problems() const noexcept { return _problems; }
239
240private:
241 template <typename... TArgs>
242 explicit deserialization_error(std::in_place_t, TArgs&&... problem_args) noexcept;
243
244private:
245 problem_list _problems;
246};
247
248/// Configuration for various deserialization options. This becomes part of the \c deserialization_context.
250{
251public:
252 using size_type = deserialization_error::problem_list::size_type;
253
254 /// When an error is encountered during deserialization, what should happen?
255 enum class on_error
256 {
257 /// Report the first problem and stop, so the \c deserialization_error thrown describes one thing that went
258 /// wrong.
259 fail_immediately,
260 /// Keep deserializing past a problem wherever something knows how to resume, so the \c deserialization_error
261 /// thrown at the end describes as many of them as it can.
262 ///
263 /// Resuming is only possible where a composite knows where its next element begins -- the next element of an
264 /// array, the next key of an object -- which is why \c deserialization_context::recover is asked rather than
265 /// told. A failure with no enclosing composite to resume into still ends deserialization with a single problem.
266 ///
267 /// Collecting gathers diagnostics; it does not produce partially-deserialized objects. A deserialization which
268 /// recovered from anything still throws, so this changes how much the error explains and never whether one
269 /// happens.
270 ///
271 /// \see deserialize_options::max_failures
272 collect_all,
273 };
274
275 /// When an object key has the same value as a previously-seen key, what should happen?
277 {
278 /// Replace the previous value with the new one. The final value of the key in the object will be the
279 /// last-encountered one.
280 ///
281 /// For example: `{ "a": 1, "a": 2, "a": 3 }` will end with `{ "a": 3 }`.
282 replace,
283 /// Ignore the new values. The final value of the key in the object will be the first-encountered one.
284 ///
285 /// For example: `{ "a": 1, "a": 2, "a": 3 }` will end with `{ "a": 1 }`.
286 ignore,
287 /// Repeated keys should raise a \c deserialization_error.
288 exception,
289 };
290
291public:
292 /// Create an instance with the default options.
294
295 /// Create a default set of options.
297 static deserialize_options create_default();
298
299 /// \{
300
301 /// See \c on_error. The default failure mode is \c fail_immediately.
303 on_error failure_mode() const noexcept { return _failure_mode; };
305 /// \}
306
307 /// \{
308
309 /// The number of problems to collect before giving up. This is only applicable if the \c failure_mode is
310 /// \c on_error::collect_all. By default, this value is 10.
311 ///
312 /// This is a threshold deserialization stops at rather than a cap on the list it reports. A single failure which
313 /// reports several problems at once -- an adapter recording a batch of them before returning, or throwing a \c
314 /// deserialization_error carrying several -- is taken whole rather than torn in half, so the final list can exceed
315 /// the limit by that batch. Truncating would drop diagnostics to enforce a bound whose purpose is to stop the walk,
316 /// not to edit the report.
317 ///
318 /// A limit of \c 0 or \c 1 makes the first problem the last, which is \c on_error::fail_immediately in all but
319 /// name.
320 ///
321 /// You should probably not set this value to an unreasonably high number, as each error encountered must be stored
322 /// in memory for some period of time.
324 size_type max_failures() const { return _max_failures; }
326 /// \}
327
328 /// \{
329
330 /// See \c duplicate_key_action. The default action is \c replace.
332 duplicate_key_action on_duplicate_key() const { return _on_duplicate_key; }
334 /// \}
335
336private:
337 // For the purposes of ABI compliance, most modifications to the variables in this class should bump the minor
338 // version number.
339 on_error _failure_mode = on_error::fail_immediately;
340 size_type _max_failures = 10U;
341 duplicate_key_action _on_duplicate_key = duplicate_key_action::replace;
342};
343
344/// A \c deserializer holds the method for converting JSON source into an arbitrary C++ type.
346{
347public:
348 virtual ~deserializer() noexcept;
349
350 /// Get the run-time type this \c deserializer knows how to deserialize. Once this \c deserializer is registered
351 /// with a
352 /// \c formats, it is not allowed to change.
354 virtual const std::type_info& get_type() const noexcept = 0;
355
356 /// Deserialize the type \a from a \c reader \a into a region of memory.
357 ///
358 /// \param context Extra information to help you decode sub-objects, such as looking up other \c deserializer
359 /// implementations via \c formats. It is also where a \ref deserialization_context::problem is
360 /// recorded and where the \c path a problem is reported at comes from.
361 /// \param from The JSON \c reader to deserialize something from. On entry, \c reader::current is the first node of
362 /// the value to deserialize -- never \c ast_node_type::document_start, which the entry points step over
363 /// before any \c deserializer runs. On a successful return it should be one position past that value, as
364 /// \c reader::next_value would leave it.
365 /// \param into The region of memory to create the deserialized object in. There will always be enough room to
366 /// create your object and the alignment of the pointer should be correct (assuming a working \c alignof
367 /// implementation).
368 ///
369 /// \returns A success result if the object was created in \a into; otherwise a \c std::unexpected carrying the
370 /// \c ast_node_type actually found when the failure was a type mismatch, or \c ast_node_type::error as a
371 /// sentinel for everything else. In the failure case nothing has been constructed in \a into and
372 /// \c deserialization_context::problems describes what went wrong.
373 ///
374 /// \see deserializer_for
375 /// \see adapter_for
376 /// \see value_adapter_for
378 virtual std::expected<void, ast_node_type>
379 deserialize(deserialization_context& context, reader& from, void* into) const = 0;
380};
381
382/// Provides extra information to routines used for deserialization, collects the problems they encounter, and tracks
383/// where in the document they are.
384///
385/// Unlike a \c serialization_context, this is mutable and single-use: recording a problem changes it. It is neither
386/// copyable nor movable, since a \ref path_scope holds a pointer to the instance it was pushed onto.
387///
388/// Most deserialization never sees one, since \c jsonv::deserialize builds its own. Build one to deserialize a document
389/// under a version, with user data, at a base path, or under the name of the file it came from (see \ref source_name),
390/// and hand it to the \c jsonv::deserialize overloads which take one -- one context for each document.
392 public context
393{
394public:
395 /// The problems recorded on a context, in the form a \c deserialization_error carries them.
396 using problem_list = deserialization_error::problem_list;
397
398 class path_scope;
399
400public:
401 /// Create a new instance using the default \c formats (\c formats::global).
403
404 /// Create a new instance using the given \a fmt, \a ver, \a p, \a userdata, \a options and \a source_name.
405 ///
406 /// \param fmt The \c formats to find a \c deserializer for each type in.
407 /// \param ver The version of the document being deserialized, for deserializers to read back with \c
408 /// context::version. \c std::nullopt means no version was specified, which is not the same thing as
409 /// version \c 0.0.
410 /// \param p A path all reported problems are relative to. This is almost always empty -- it exists for
411 /// deserialization of a document which is itself a fragment of some larger one.
412 /// \param userdata Arbitrary data for deserializers to read back with \c context::user_data. It is not owned, so it
413 /// must outlive this instance.
414 /// \param options What to do when something goes wrong. The default reports the first problem and stops; see
415 /// \c deserialize_options::on_error.
416 /// \param source_name The name of the document being deserialized, such as the file it was read from, for every
417 /// problem recorded here to be reported in. Empty, the default, names nothing. Spell out every
418 /// argument before it: a string literal in the place of \a userdata is a <tt>const void*</tt> as
419 /// far as the compiler is concerned, and would quietly become the user data instead.
421 std::optional<jsonv::version> ver = std::nullopt,
423 const void* userdata = nullptr,
425 std::string source_name = std::string()
426 );
427
429 deserialization_context& operator=(const deserialization_context&) = delete;
430
431 virtual ~deserialization_context() noexcept;
432
433 /// Is the source being deserialized storage which is freed when deserialization finishes, rather than storage the
434 /// caller keeps?
435 ///
436 /// A deserializer which returns a view of what it was given must check this and refuse when it is \c true, because
437 /// the storage its view would name is gone by the time the caller has it. \c std::string_view is the built-in one.
438 /// There are three ways to get here:
439 ///
440 /// - A \c value -based adapter runs against a reader over JSON text. There is no pre-existing tree for it to
441 /// borrow, so one is materialised and destroyed as the bridge unwinds. This is true for everything nested under
442 /// such a materialisation, not only the value that caused it. - The source was handed to deserialization to own:
443 /// \c jsonv::deserialize given a \c std::string rvalue, or an rvalue \c reader which owns its source. That
444 /// source dies with the call, so this is true for the whole deserialization. - A hook of a type described with the
445 /// serialization builder DSL has been lent, by \ref source_value, an object read out of JSON text for it. That
446 /// object dies with the deserialization of the type, so this is true from then until the walk of the object
447 /// starts or its deserialization finishes.
449 bool source_is_temporary() const noexcept { return _temporary_source_depth != 0U; }
450
451 /// Get the options this context is deserializing under.
453 const deserialize_options& options() const noexcept { return _options; }
454
455 /// Get the name of the document being deserialized, which every problem recorded here is reported in. It is empty
456 /// if this context was not given one.
457 ///
458 /// \see deserialization_error::problem::source_name
460 const std::string& source_name() const noexcept { return _source_name; }
461
462 /// Get the JSON of the object a type described with the serialization builder DSL is being deserialized from, from
463 /// its
464 /// \c { to its matching \c }. Where \ref source_name says which document a problem is in, this quotes the object it
465 /// is about, for the hooks which validate one to put in their message.
466 ///
467 /// It is only there once the walk has reached the object's \c }, which is to say for what runs after it:
468 /// \c on_unknown_members, every member's \c default_value and the setter that default is handed to, and
469 /// \c post_deserialize. What runs before or during the walk is shown nothing: \c pre_deserialize, which can have
470 /// the object as a \c value from \ref source_value but not its text, and a member's \c check and setter as its key
471 /// is read. Neither is anything a hook goes on to deserialize through \ref deserialize -- or through
472 /// \c jsonv::deserialize, which comes through it -- whichever deserializer that reaches, nor anything outside DSL
473 /// deserialization altogether. No object's source is empty -- the least of them is \c {} -- so an empty view always
474 /// means there is nothing to quote.
475 ///
476 /// A deserializer reached around \ref deserialize, through \c formats::deserialize or by calling it directly, skips
477 /// the hiding along with everything else \ref deserialize does around the call, and is shown whatever is showing.
478 ///
479 /// Read from JSON text, this is a view of exactly what was written, whitespace, comments and keys no member claimed
480 /// included. Read from a \c value, there was never any text to view, so it is that value's compact encoding --
481 /// faithful to the JSON, but not necessarily the bytes anybody typed. It is encoded on the first call and kept for
482 /// the rest of that object's hooks, so a deserialization which never asks never pays for it.
483 ///
484 /// \returns A view which is valid until the hook which asked for it returns.
486 std::string_view encoded_source() const;
487
488 /// Get the value a type described with the serialization builder DSL is being deserialized from, for its hooks to
489 /// read members out of: a version for \c pre_deserialize to refuse a document by, a sibling for a \c default_value
490 /// to compute from, the values of the keys an \c on_unknown_members handler is told no member claimed.
491 ///
492 /// It is there for the hooks which take a \c deserialization_context: \c pre_deserialize, before the walk, is shown
493 /// the value the reader is on, which is not necessarily an object; and \c on_unknown_members, every member's
494 /// \c default_value and \c post_deserialize, after it, are shown the object. A member's \c check and setter run
495 /// during the walk and are shown nothing, as is a \c type_default_value standing in for a \c null. So, as for \ref
496 /// encoded_source, is anything a hook goes on to deserialize through \ref deserialize, and anything outside DSL
497 /// deserialization altogether.
498 ///
499 /// Read from a \c value, this is that value: the caller's own tree, lent rather than copied. Read from JSON text
500 /// there is no tree to lend, so the first hook to ask has the object read into one -- from where the reader is,
501 /// before the walk, or from the object's \c { after it, through a second cursor which leaves the first where it
502 /// is. Every hook of that object after it is shown the same one. Nothing is read for a deserialization which never
503 /// asks, and nothing allocated: what lets the hooks after the walk go back is a position on the parsed document's
504 /// tape. A repeated key is settled by \ref options as the walk settles it.
505 ///
506 /// What is read from text belongs to the deserialization rather than to the caller, so while it is being lent
507 /// \ref source_is_temporary is \c true -- and deserializing a \c std::string_view out of it is refused rather than
508 /// left to dangle. That lasts until the walk starts or the object is finished.
509 ///
510 /// \returns The value, valid until the hook which asked for it returns; or nothing where no hook is being shown
511 /// one.
512 /// \throws Whatever \c read_value throws for the object, the first time it is read from text.
515
516 /// Get the path currently being deserialized, as named by the live \ref path_scope guards.
517 ///
518 /// This is built on demand by walking the scope chain, so it is not free -- but nothing on a successful
519 /// deserialization calls it. If no scope is live the result is the base path this context was created with, which
520 /// is usually empty; see \ref path_scope for why that is not the same as "the root of the document".
523
524 /// Note that a problem has been encountered, forwarding \a args to a \c deserialization_error::problem.
525 ///
526 /// \returns \c std::unexpected of \c ast_node_type::error in all cases, which converts implicitly into any
527 /// \c std::expected<T, ast_node_type>, so an implementation can simply return it:
528 ///
529 /// \code
530 /// if (*result < 500 || *result > 2500)
531 /// return context.problem(context.path(), "Expected a value between 500 and 2500");
532 /// \endcode
533 ///
534 /// Recording a problem does not throw. The entry point which started deserialization throws a single
535 /// \c deserialization_error carrying everything collected, once the pipeline has unwound.
536 ///
537 /// A problem which does not already name its source is recorded as being in this context's \ref source_name. One
538 /// folded in from the deserialization of some other document keeps the name it was given there.
539 template <typename... TArgs>
541 std::unexpected<ast_node_type> problem(TArgs&&... args)
542 {
543 // Copied before anything is recorded, so a copy which fails leaves no problem behind without its name.
544 std::string name = _source_name;
545 auto& recorded = _problems.emplace_back(std::forward<TArgs>(args)...);
546 if (recorded._source_name.empty())
547 recorded._source_name = std::move(name);
548
549 return std::unexpected(ast_node_type::error);
550 }
551
552 /// \{
553
554 /// Get the problems encountered so far. If this list is empty, no problems have occurred.
556 const problem_list& problems() const& { return _problems; }
558 problem_list&& problems() && { return std::move(_problems); }
559 /// \}
560
561 /// \{
562
563 /// May deserialization recover from a failure and keep going?
564 ///
565 /// A composite which knows where its next element begins -- the next element of an array, the next key of an
566 /// object -- asks this when one of them fails. A \c true answer means skip what failed and keep walking, so one
567 /// bad element does not hide every problem after it; \c false means report the failure and let the pipeline
568 /// unwind. Only the loop knows where it would resume, which is why collecting is something a composite opts into
569 /// rather than something this context can deliver on its own -- and why a failure with no enclosing composite
570 /// ends deserialization however \c deserialize_options::failure_mode is set.
571 ///
572 /// The answer is \c false under \c deserialize_options::on_error::fail_immediately, and becomes \c false in
573 /// \c collect_all once \c deserialize_options::max_failures problems have been recorded. It is never a promise that
574 /// deserialization will succeed: recovering collects diagnostics, it does not produce partial objects, so a
575 /// composite which recovered from anything **must still report failure** once its loop is done.
576 ///
577 /// \code
578 /// auto element = context.deserialize<T>(from);
579 /// if (!element)
580 /// {
581 /// if (!context.recover())
582 /// return std::unexpected(element.error());
583 ///
584 /// recovered = true;
585 /// context.skip_failed_value(from);
586 /// continue;
587 /// }
588 /// \endcode
589 ///
590 /// Note \ref skip_failed_value rather than \c reader::next_value: where the value which failed was read through
591 /// the \c value bridge, the cursor is already past it and stepping again would skip the next one.
592 ///
593 /// The overload taking a \c deserialization_error is for an adapter on the \c value bridge, which reports failure
594 /// by throwing. On \c true the problems \a ex carries have been folded onto this context and the caller may
595 /// continue; on \c false nothing was folded and the caller should rethrow \a ex, which the catch in \c
596 /// deserialize(const std::type_info&, reader&, void*) folds instead. Either way every problem is recorded exactly
597 /// once, which is the thing to preserve: the \c value -based overloads hand their problems to the exception rather
598 /// than leaving them behind, so a fold in both places would report each failure twice.
600 bool recover() const noexcept;
602 bool recover(const deserialization_error& ex);
603 /// \}
604
605 /// Remove and return the problems recorded since \a mark, a value \c problems() previously reported the size of.
606 ///
607 /// A composite which recovered still has to report failure, and on the \c value -based interface that means
608 /// throwing a \c deserialization_error. This is how it hands over what it collected without leaving a copy behind
609 /// for the catch which folds that error back onto a context to record a second time.
611 problem_list take_problems_since(problem_list::size_type mark);
612
613 /// Step \a from past the value whose failure is being recovered from.
614 ///
615 /// This is \c reader::next_value, except where the step has already happened. Most deserializers leave the cursor
616 /// naming the value they rejected, so stepping over it is exactly one \c reader::next_value. An adapter on the
617 /// \c value bridge reading a structure out of JSON text is the exception: materialising that structure is what
618 /// walks the cursor over it, so by the time the older body reports a failure the cursor names the *next* sibling.
619 /// A loop recovering with a bare \c reader::next_value steps over that sibling as well, dropping it from the
620 /// result and dropping every problem it had to report -- and misnumbering everything after it.
621 ///
622 /// The note this consults belongs to \a from and to the one failure being reported. It is cleared when the next
623 /// deserialization starts, so a note nobody collects expires rather than answering for an unrelated position.
624 ///
625 /// \see recover
626 /// \see note_value_consumed
627 void skip_failed_value(reader& from) noexcept;
628
629 /// Note that the failure about to be reported has already consumed, from \a from, the value it failed on, so
630 /// whatever recovers from it must not step over that value a second time.
631 ///
632 /// The \c value bridge says this for itself. An adapter which walks the reader has to say it whenever it fails
633 /// with the value behind it rather than in front of it: after a nested deserialization which succeeded, or once it
634 /// has read its own closing token. A composite which fails part-way through a structure should finish walking
635 /// that structure first -- the position inside it means nothing to a caller -- and then say so.
636 ///
637 /// \see skip_failed_value
638 void note_value_consumed(const reader& from) noexcept;
639
640 /// \{
641
642 /// Check that the \c reader::current AST node of \a from has the given \a type or is one of the given \a types. If
643 /// it is not, a \ref problem describing the mismatch is recorded and the type actually found is returned.
644 ///
645 /// This is \c reader::expect plus the human-readable message, which lives here because this is the layer that has
646 /// the path and the problem list to attach it to.
647 ///
648 /// \see current_as
649 /// \see reader::expect
651 std::expected<void, ast_node_type> expect(reader& from, ast_node_type type);
653 std::expected<void, ast_node_type> expect(reader& from, std::initializer_list<ast_node_type> types);
654 /// \}
655
656 /// Get the \c reader::current AST node of \a from as a \c TAstNode, recording a \ref problem if it is some other
657 /// type.
658 ///
659 /// \see expect
660 /// \see reader::current_as
661 template <typename TAstNode>
663 std::expected<TAstNode, ast_node_type> current_as(reader& from)
664 {
665 // Written as an explicit branch rather than `expect(...).transform(...)` for the same reason
666 // `reader::current_as` is: the monadic operations on `std::expected` postdate the type, so using one here
667 // would quietly raise the minimum toolchain by a release.
668 if (auto matched = expect(from, TAstNode::type()); !matched)
669 return std::unexpected(matched.error());
670 else
671 return from.current().as<TAstNode>();
672 }
673
674 /// Where to report a problem noticed while \a from is sitting on the thing that is wrong.
675 ///
676 /// This is \ref path when any \ref path_scope has named a position and the reader's own \c reader::current_path
677 /// when none has -- never both, since an adapter walking a single reader would otherwise have its position
678 /// counted twice. \ref expect and \ref current_as report through this; a deserializer which rejects a value for a
679 /// reason other than its node type -- a number outside the range of what it builds, say -- wants the same answer
680 /// for the same reason.
681 ///
682 /// It is not free: on a text-backed reader with no scope live, \c reader::current_path rescans from the start of
683 /// the document. Ask for it when recording a problem, not before one happens.
685 jsonv::path problem_path(const reader& from) const;
686
687 /// Attempt to deserialize a \c T from \a from using the \c formats associated with this context.
688 ///
689 /// This is the positioned primitive a composite calls for each of its parts: it deserializes the value under the
690 /// cursor and nothing else. In particular it does not step over \c ast_node_type::document_start, so it is not
691 /// the way to start on a fresh reader -- \c jsonv::deserialize is.
692 ///
693 /// \tparam T is the type to deserialize. It must be movable.
694 template <typename T>
696 std::expected<T, ast_node_type> deserialize(reader& from)
697 {
698 alignas(T) std::byte place[sizeof(T)];
699 if (auto result = deserialize(typeid(T), from, static_cast<void*>(place)); !result)
700 return std::unexpected(result.error());
701
702 T* ptr = std::launder(reinterpret_cast<T*>(place));
703 auto destroy = detail::on_scope_exit([ptr] { std::destroy_at(ptr); });
704 return std::move(*ptr);
705 }
706
707 /// Attempt to deserialize an object of the given \a type from \a from into the memory at \a into, using the
708 /// \c formats associated with this context. This is what the overload above calls with the \c T it was asked for.
709 ///
710 /// \a into must have room for an object of \a type and be suitably aligned for it. On success an object has been
711 /// created there, and destroying it is up to the caller. On failure nothing has been created, the problem is
712 /// recorded on this context, and the \c ast_node_type returned is the one the \c deserializer reported (see \c
713 /// deserializer::deserialize). An exception thrown while deserializing is not propagated: it is recorded as a
714 /// problem and reported as \c ast_node_type::error, except that a \c deserialization_error from an adapter on the
715 /// \c value bridge has its own problems folded onto this context instead.
716 ///
717 /// Called from a hook which can see \ref source_value or \ref encoded_source, this hides both from everything the
718 /// deserialization runs, since none of that is part of the object the hook is about.
720 std::expected<void, ast_node_type> deserialize(const std::type_info& type, reader& from, void* into);
721
722 /// Attempt to deserialize a \c T from the in-memory \a from using the \c formats associated with this context.
723 ///
724 /// This runs the same pipeline as the \c reader overload by walking \a from through a \c reader::from_value, and
725 /// reports failure by throwing rather than by returning. It is how an adapter written against the older
726 /// \c value-based interface reaches the rest of the pipeline. To deserialize part of \a from, name the part --
727 /// <tt>deserialize<T>(from.at("a"))</tt> -- under a \ref path_scope saying where it is.
728 ///
729 /// \throws deserialization_error if anything goes wrong when attempting to deserialize a value.
730 ///
731 /// \see value_adapter_for
732 template <typename T>
734 T deserialize(const value& from);
735
736 /// An RAII guard naming one step of the deserialization path while it is alive.
737 ///
738 /// A \c reader knows where the *reader* is, which is not always where the *deserializer* is: an adapter which
739 /// re-roots onto a subtree gets a reader whose \c reader::current_path is relative to that subtree, and an adapter
740 /// which renames a member wants the name the caller declared rather than the one the document used. Pushing a
741 /// scope says where the deserializer is, and takes precedence over the reader's own answer.
742 ///
743 /// Scopes are kept on the C++ stack and linked into a chain, so a push is two stores and a pop is one. No \c
744 /// jsonv::path is built until something calls \c deserialization_context::path, which happens only when a problem
745 /// is recorded.
746 ///
747 /// The overloads which view their argument allocate nothing: the \c std::size_t one, and those naming a key by
748 /// \c std::string_view, \c std::string lvalue or string literal. A key they view must outlive the scope, which is
749 /// why an owning \c path_element overload exists for the callers that cannot promise it (a key decoded from an
750 /// \c ast_node_type::key_escaped node, for instance). A \c std::string or \c char array rvalue, \c const or not, is
751 /// owned the same way rather than viewed, since it would be gone before the scope is. A <tt>const char*</tt>
752 /// variable is ambiguous between the \c std::string_view and \c path_element overloads; spell it
753 /// <tt>std::string_view(key)</tt>.
755 {
756 public:
757 /// Name the element at \a index of the array being deserialized, on \a context.
758 path_scope(deserialization_context& context, std::size_t index) noexcept;
759
760 /// Name the member \a key of the object being deserialized, on \a context. \a key is viewed rather than copied.
761 path_scope(deserialization_context& context, std::string_view key) noexcept;
762
763 /// Name the member \a key of the object being deserialized, on \a context. \a key is a string literal, or some
764 /// other array holding a NUL-terminated string, and is viewed rather than copied.
765 template <std::size_t N>
766 path_scope(deserialization_context& context, const char (&key)[N]) noexcept :
767 path_scope(context, std::string_view(key))
768 {
769 // An array rather than a `const char*`, which a literal `0` converts to as readily as it does to the
770 // `std::size_t` overload's index: GCC calls that ambiguous and Clang does not. The key is measured rather
771 // than taken to be `N - 1` long, since an array of `char` with room to spare binds here as well.
772 }
773
774 /// Name the member \a key of the object being deserialized, on \a context, keeping a copy of \a key for as long
775 /// as this scope lives. \a key is an array of \c char holding a NUL-terminated string which is about to be
776 /// destroyed, such as a member of a temporary.
777 template <std::size_t N>
778 path_scope(deserialization_context& context, const char (&&key)[N]) :
779 path_scope(context, path_element(std::string_view(key)))
780 { }
781
782 /// Name the member \a key of the object being deserialized, on \a context. \a key is a \c std::string, and is
783 /// viewed rather than copied.
784 template <typename TString>
785 requires std::same_as<TString, std::string>
786 path_scope(deserialization_context& context, const TString& key) noexcept :
787 path_scope(context, std::string_view(key))
788 {
789 // A template rather than a `const std::string&` so that a braced `{ data, size }`, which deduces nothing,
790 // goes on reaching the `std::string_view` overload alone instead of being ambiguous with this one.
791 }
792
793 /// Name the member \a key of the object being deserialized, on \a context, keeping \a key for as long as this
794 /// scope lives. \a key is a \c std::string rvalue. One which is \c const, such as one returned as a
795 /// <tt>const std::string</tt>, cannot be moved from, so it is copied rather than viewed after it is gone.
796 template <typename TString>
797 requires std::same_as<std::remove_const_t<TString>, std::string>
799 path_scope(context, path_element(std::forward<TString>(key)))
800 { }
801
802 /// Name \a elem on \a context, keeping a copy of it for as long as this scope lives.
804
805 path_scope(const path_scope&) = delete;
806 path_scope& operator=(const path_scope&) = delete;
807
808 ~path_scope() noexcept;
809
810 private:
811 friend class deserialization_context;
812
813 /// Append this scope's ancestors and then itself to \a out, so the result reads outermost-first.
814 void append_to(jsonv::path& out) const;
815
816 private:
817 deserialization_context* _context;
818 const path_scope* _parent;
819 std::variant<std::size_t, std::string_view, path_element> _element;
820 };
821
822private:
823 friend class path_scope;
824 friend class detail::borrowed_subtree;
825 friend class detail::source_scope;
826 friend class detail::temporary_source_scope;
827
828 friend JSONV_PUBLIC void detail::deserialize_entry(deserialization_context& context,
829 const std::type_info& type,
830 reader& from,
831 void* into,
832 void (* destroy)(void*) noexcept,
833 detail::source_lifetime lifetime
834 );
835
836 /// Where to report a failure which is being translated out of an exception. Unlike \ref problem_path this takes
837 /// the location a bridge left behind on its way out, because by now the cursor has moved on from the value which
838 /// failed. Taking it is the point: it belongs to the failure being translated and to nothing after it.
840 jsonv::path take_failure_path(const reader& from);
841
842private:
843 deserialize_options _options;
844 jsonv::path _base_path;
845 std::string _source_name;
846 const path_scope* _innermost = nullptr;
847 std::size_t _temporary_source_depth = 0U;
848
849 /// The object \ref source_value and \ref encoded_source answer for, if one is being deserialized.
850 const detail::source_scope* _innermost_source = nullptr;
851
852 /// Where to report the failure currently unwinding, left by a bridge which walked the cursor past the value which
853 /// failed. A destructor runs before the handler which records the problem, so the location has to be worked out
854 /// in the destructor and picked up by \ref take_failure_path. It lives and dies with one call to \c deserialize.
855 std::optional<jsonv::path> _failure_path;
856
857 /// The reader whose cursor a bridge already walked past the value currently failing. Read by
858 /// \ref skip_failed_value and, like \ref _failure_path, it lives and dies with one call to \c deserialize.
859 const reader* _consumed_failed_value = nullptr;
860
861 problem_list _problems;
862};
863
864/// Consume the JSON subtree under \a from starting at \c reader::current and return it as a fully materialised \c value
865/// tree.
866///
867/// On return \a from has advanced one position past the consumed subtree, exactly as \c reader::next_value would have
868/// left it for the same input. Every adapter reading a subtree has to agree on this, or subtrees get consumed twice or
869/// not at all. A leading \c ast_node_type::document_start is stepped over first, so this works on a freshly-created
870/// reader as well as on one positioned mid-document.
871///
872/// This is the bridge which lets adapters written against the older \c value -based interface keep working while the
873/// surrounding pipeline runs against a streaming \c reader.
874///
875/// A structure which fails part-way through is still stepped over, so \a from is left one past it just as success
876/// would have left it; a scalar which fails leaves \a from on it. A deserializer which lets the failure of a structure
877/// out has to say that the value is behind the cursor, or a composite recovering from it steps over the following
878/// sibling as well -- which is what the overload taking a \c deserialization_context is for.
879///
880/// An object which repeats a key keeps the last of its values, which is \c deserialize_options::duplicate_key_action's
881/// default. The overload taking a \c deserialization_context does what its options say instead.
882///
883/// \throws deserialization_error if \a from is not positioned on a value or the document ends part-way through one.
884/// \throws std::invalid_argument if a number has no finite \c double to round to, such as \c 1e400.
885/// \throws parse_error if a string holds an escape which does not decode, such as an unpaired `\uD800`.
886///
887/// \see value_adapter_for
889
890/// \ref read_value for a deserializer. When a structure fails, which leaves the cursor past it, this also says so to \a
891/// context with \c deserialization_context::note_value_consumed -- which is what lets a composite recovering from the
892/// failure resume at the next sibling rather than one sibling too far.
893///
894/// A repeated key is settled by \a context's \c deserialize_options::on_duplicate_key, as \c parse settles it for the
895/// same options: \c replace keeps the last value, \c ignore the first, and \c exception refuses the object.
896///
897/// A failure is placed where \a context says, as any other problem is: under its base path or a live
898/// \c deserialization_context::path_scope when it has one, followed by whatever lies below where the read began. With
899/// neither, the reader's own path stands -- and a reader which has run out has none to give.
900///
901/// \throws deserialization_error for a repeated key under \c deserialize_options::duplicate_key_action::exception, as
902/// well as for everything \ref read_value throws it for.
904
905namespace detail
906{
907
908/// \ref read_value without moving \a from: the subtree under its cursor is read through a second cursor on the same
909/// source, which is what lets a deserializer decide what to deserialize before deserializing it. A scalar under a
910/// cursor over JSON text is read where it sits, since there is nothing to walk.
911///
912/// \param context The deserialization this is peeking for. What is peeked at is going to be read again for real, and
913/// the two have to agree about which value a repeated key has, so one is settled by \a context's
914/// \c deserialize_options::on_duplicate_key exactly as \ref read_value settles it for \a context -- and a
915/// failure is placed where \a context says, as \ref read_value places one.
916///
917/// \throws Whatever \ref read_value throws for the same subtree, including for a repeated key under
918/// \c deserialize_options::duplicate_key_action::exception.
920
921/// The members named in \a keys of the object under \a from's cursor, read without moving \a from and without reading
922/// any other member -- each of those is stepped over whole, which on a reader over text costs nothing however large it
923/// is.
924///
925/// \param context The deserialization this is peeking for, as for \ref peek_value. Only the keys asked for are looked
926/// at, so only a repeat of one of those is settled -- or refused.
927///
928/// A document which ends part-way through the object gives back whatever was found before it ended.
929///
930/// \returns An object holding the members found; or \c null if \a from is not on an object.
931/// \throws Whatever \ref read_value throws for one of the members read, including for a repeat of one of \a keys
932/// under \c deserialize_options::duplicate_key_action::exception.
934 const reader& from,
935 const std::set<std::string, std::less<>>& keys
936 );
937
938/// Where \a from is, for \ref peek_value_at to read the value there once \a from has moved past it.
939///
940/// \returns The position; or nothing if \a from is not over JSON text. A reader over a \c value has the value itself to
941/// lend, through \c reader::current_value, and needs no way back to it.
942JSONV_NODISCARD JSONV_PUBLIC std::optional<parse_index::const_iterator> bookmark(const reader& from) noexcept;
943
944/// \ref peek_value for the value at \a at, a position \ref bookmark gave for \a from, wherever \a from has got to
945/// since. It is read through a second cursor on \a from's source, so \a from does not move.
946///
947/// \throws Whatever \ref peek_value throws for the same subtree.
949 const reader& from,
951 );
952
953/// Say, for as long as this lives, that what is being deserialized from is a temporary -- see
954/// \c deserialization_context::source_is_temporary.
955///
956/// A deserializer which builds a \c value and shows it to a callback has made exactly the temporary that question is
957/// about, and nothing the callback deserializes from it can tell: \c deserialization_context::deserialize(const value&)
958/// takes the
959/// \c value to be the caller's. \c polymorphic_adapter does this when it materialises a subtree read from JSON text
960/// for its discriminators.
961class temporary_source_scope
962{
963public:
964 explicit temporary_source_scope(deserialization_context& context) noexcept :
965 _context(&context)
966 {
967 ++_context->_temporary_source_depth;
968 }
969
970 temporary_source_scope(const temporary_source_scope&) = delete;
971 temporary_source_scope& operator=(const temporary_source_scope&) = delete;
972
973 ~temporary_source_scope() noexcept
974 {
975 --_context->_temporary_source_depth;
976 }
977
978private:
979 deserialization_context* _context;
980};
981
982/// Say, for as long as this lives, which object \c deserialization_context::source_value and
983/// \c deserialization_context::encoded_source answer for, and whether they answer at all.
984///
985/// The serialization builder's adapter makes one for each object it deserializes, before \c pre_deserialize runs, on
986/// the value its reader is on. That shows the value but not its text, since nothing knows where it ends until it has
987/// been walked. \c open hides both for the walk, and \c close shows both for what runs after it. \c
988/// deserialization_context::deserialize makes one which shows nothing around any deserialization started while
989/// something is showing -- by one of those hooks, deserializing something else -- so that nothing the second
990/// deserialization runs, whichever deserializer runs it, is shown an object it is not part of.
991///
992/// Like a \c deserialization_context::path_scope, this is linked into the context rather than copied into it, and it
993/// records where its object is rather than what is in it: that is what lets a deserialization which never asks pay
994/// nothing.
995class source_scope
996{
997public:
998 /// Show nothing, hiding whatever an enclosing scope was showing.
999 explicit source_scope(deserialization_context& context) noexcept :
1000 _context(&context),
1001 _parent(context._innermost_source)
1002 {
1003 context._innermost_source = this;
1004 }
1005
1006 /// Show the value \a from is on, which is what the hooks which run before the walk are about. The reader has to
1007 /// still be on it when one of them asks.
1008 source_scope(deserialization_context& context, const reader& from) noexcept :
1009 source_scope(context)
1010 {
1011 _from = &from;
1012 _tree = from.current_value();
1013 _showing = shows::value;
1014 }
1015
1016 source_scope(const source_scope&) = delete;
1017 source_scope& operator=(const source_scope&) = delete;
1018
1019 ~source_scope() noexcept
1020 {
1021 stop_lending();
1022 _context->_innermost_source = _parent;
1023 }
1024
1025 /// Show nothing from here on.
1026 void hide() noexcept
1027 {
1028 stop_lending();
1029 _showing = shows::nothing;
1030 }
1031
1032 /// Note the object \a from is on the \a opened \c { of, and show nothing while it is walked.
1033 void open(const reader& from, const ast_node::object_begin& opened) noexcept
1034 {
1035 hide();
1036
1037 // A reader over a `value` lends the object, and needs neither the text nor a way back to it.
1038 if (_tree)
1039 return;
1040
1041 _begin = opened.token_raw().data();
1042
1043 // The way back for a hook after the walk, when the reader will be on the `}`. Not needed if a hook before the
1044 // walk has already read the object, which is the same object.
1045 if (!_read)
1046 _bookmark = detail::bookmark(from);
1047 }
1048
1049 /// Note that \a from has reached the \c } of the object, and show all of it from here on.
1050 void close(const reader& from)
1051 {
1052 // Text only. A reader over a `value` synthesises its `{` and `}` out of one static string, where they sit side
1053 // by side -- so the same arithmetic would not fail there, it would quietly quote every object as `{}`.
1054 if (!_tree)
1055 {
1056 auto end = from.current().token_raw();
1057 _text = std::string_view(_begin, static_cast<std::size_t>(end.data() + end.size() - _begin));
1058 }
1059
1060 _showing = shows::value_and_text;
1061 }
1062
1063private:
1064 friend class jsonv::deserialization_context;
1065
1066 enum class shows : unsigned char
1067 {
1068 nothing,
1069 value,
1070 value_and_text,
1071 };
1072
1073 /// Say that what \c deserialization_context::source_value is lending was read out of text and dies with this scope,
1074 /// so that nothing deserialized from it is a view of it. Once is enough: it stays said until \ref stop_lending.
1075 void lend_temporary() const noexcept
1076 {
1077 if (!_lending)
1078 {
1079 _lending = true;
1080 ++_context->_temporary_source_depth;
1081 }
1082 }
1083
1084 void stop_lending() const noexcept
1085 {
1086 if (_lending)
1087 {
1088 _lending = false;
1089 --_context->_temporary_source_depth;
1090 }
1091 }
1092
1093private:
1094 deserialization_context* _context;
1095 const source_scope* _parent;
1096 const reader* _from = nullptr;
1098 std::optional<parse_index::const_iterator> _bookmark;
1099 const char* _begin = nullptr;
1100 std::string_view _text;
1101 shows _showing = shows::nothing;
1102 mutable bool _lending = false;
1103
1104 /// The object read out of text, once something has asked for it. Not a bare \c value, which would be a \c null
1105 /// built for every object deserialized.
1106 mutable std::optional<value> _read;
1107
1108 /// The encoding of \c _tree, once something has asked for it. Not a bare `std::string`, since default-constructing
1109 /// one is not free on every standard library and this is made for every object deserialized.
1110 mutable std::optional<std::string> _encoded;
1111};
1112
1113/// The subtree under a reader's cursor as a \c value, borrowed rather than copied when the reader can lend it.
1114///
1115/// A reader created by \c reader::from_value is already holding the tree the older \c value -based interface wants.
1116/// Materialising a copy for it would pay for a deep copy and, worse, hand the adapter storage which dies with this
1117/// object -- silently breaking every deserializer which returns a view of what it was given. Borrowing is what keeps a
1118/// \c std::string_view pointing into the caller's \c value, which is where it pointed before deserialization ran
1119/// through a \c reader.
1120///
1121/// A reader over JSON text has no such tree, so the subtree is materialised. Anything borrowed from it is valid only
1122/// until this object goes away, which is why the context is told: see \c deserialization_context::source_is_temporary.
1123///
1124/// **The reader is not advanced until \c commit.** An adapter on the bridge consumes its whole subtree before the
1125/// older body runs, so a failure in that body would otherwise be reported against the next sibling. Leaving the cursor
1126/// where the deserialization started means the position is simply still correct, which is cheaper and more accurate
1127/// than noting it beforehand -- \c reader::current_path rebuilds by scanning from the start of the document on a
1128/// text-backed source, so asking for it on every successful deserialization is quadratic. A structure read from text is
1129/// the one case which cannot wait, since materialising it is what walks the cursor over it.
1130class JSONV_PUBLIC borrowed_subtree
1131{
1132public:
1133 borrowed_subtree(deserialization_context& context, reader& from);
1134
1135 borrowed_subtree(const borrowed_subtree&) = delete;
1136 borrowed_subtree& operator=(const borrowed_subtree&) = delete;
1137
1138 ~borrowed_subtree() noexcept;
1139
1141 const value& get() const noexcept { return _borrowed ? *_borrowed : _owned; }
1142
1143 /// Step the reader past the subtree, if it is not already past it. Call this once the older body has succeeded;
1144 /// skipping it on failure is what leaves the cursor naming the value which failed.
1145 void commit();
1146
1147private:
1148 deserialization_context* _context;
1149 reader* _from;
1150 optional<const value&> _borrowed;
1151 bool _materialised;
1152 bool _advanced;
1153 /// Distinct from \ref _advanced: a structure read out of text is advanced by the constructor, so the two only
1154 /// agree for the shapes \c commit had something left to do for.
1155 bool _committed;
1156 int _uncaught_on_entry;
1157 value _owned;
1158};
1159
1160/// Call \a func as a deserialization function and normalise whatever it gives back into a \c std::expected.
1161///
1162/// Four call shapes are accepted, tried in this order: <tt>(context, reader)</tt>, <tt>(reader)</tt>,
1163/// <tt>(context, value)</tt>, <tt>(value)</tt>. The last two are the interface functions were written against before
1164/// deserialization ran off a \c reader; they get a subtree materialised by \c read_value. Either a bare \c T or a
1165/// \c std::expected<T, ast_node_type> is an acceptable return.
1166template <typename T, typename FDeserialize>
1168std::expected<T, ast_node_type>
1169invoke_deserialize(const FDeserialize& func, deserialization_context& context, reader& from)
1170{
1171 auto normalise = [](auto&& result) -> std::expected<T, ast_node_type>
1172 {
1173 if constexpr (is_expected_v<std::remove_cvref_t<decltype(result)>>)
1174 {
1175 if (result)
1176 return std::forward<decltype(result)>(result).value();
1177 else
1178 return std::unexpected(result.error());
1179 }
1180 else
1181 {
1182 return std::forward<decltype(result)>(result);
1183 }
1184 };
1185
1186 // The reader shapes consume the value themselves, so by the time `func` has returned the cursor is past it and
1187 // normalising -- which moves the result into the pipeline's `std::expected`, using the caller's own move
1188 // constructor -- is failing with that value behind it. Anything thrown *by* `func` is deliberately left alone:
1189 // it may have failed before consuming anything, and the call is evaluated outside the guard for that reason.
1190 auto normalise_consumed = [&] (auto&& raw) -> std::expected<T, ast_node_type>
1191 {
1192 try
1193 {
1194 return normalise(std::forward<decltype(raw)>(raw));
1195 }
1196 catch (...)
1197 {
1198 context.note_value_consumed(from);
1199 throw;
1200 }
1201 };
1202
1203 if constexpr (std::invocable<const FDeserialize&, deserialization_context&, reader&>)
1204 {
1205 return normalise_consumed(func(context, from));
1206 }
1207 else if constexpr (std::invocable<const FDeserialize&, reader&>)
1208 {
1209 return normalise_consumed(func(from));
1210 }
1211 else if constexpr (std::invocable<const FDeserialize&, deserialization_context&, const value&>)
1212 {
1213 // `get()` is a `const value&`, which is the signature the `invocable` check above tested. Handing over a
1214 // mutable one would let a callable overloaded on both pick the other overload.
1215 borrowed_subtree subtree(context, from);
1216 auto result = normalise(func(context, subtree.get()));
1217 if (!result)
1218 return result;
1219
1220 // Committing steps the cursor past the value, so the named return -- which moves the result with the
1221 // caller's own move constructor -- fails with that value behind it. `borrowed_subtree` cannot say so on our
1222 // behalf here: it has committed, which is the state it takes to mean the body succeeded.
1223 subtree.commit();
1224 try
1225 {
1226 return result;
1227 }
1228 catch (...)
1229 {
1230 context.note_value_consumed(from);
1231 throw;
1232 }
1233 }
1234 else
1235 {
1236 static_assert(std::invocable<const FDeserialize&, const value&>,
1237 "A deserialization function must be callable as (deserialization_context&, reader&), (reader&), "
1238 "(deserialization_context&, const value&) or (const value&)"
1239 );
1240
1241 borrowed_subtree subtree(context, from);
1242 auto result = normalise(func(subtree.get()));
1243 if (!result)
1244 return result;
1245
1246 // Committing steps the cursor past the value, so the named return -- which moves the result with the
1247 // caller's own move constructor -- fails with that value behind it. `borrowed_subtree` cannot say so on our
1248 // behalf here: it has committed, which is the state it takes to mean the body succeeded.
1249 subtree.commit();
1250 try
1251 {
1252 return result;
1253 }
1254 catch (...)
1255 {
1256 context.note_value_consumed(from);
1257 throw;
1258 }
1259 }
1260}
1261
1262/// The type a deserialization function deserializes: its return type, with a \c std::expected unwrapped, deduced from
1263/// the same four call shapes \c invoke_deserialize accepts and in the same order.
1264template <typename FDeserialize>
1265struct deserialize_function_result
1266{
1267 static auto deduce()
1268 {
1269 if constexpr (std::invocable<const FDeserialize&, deserialization_context&, reader&>)
1270 return std::type_identity<std::invoke_result_t<const FDeserialize&, deserialization_context&, reader&>>();
1271 else if constexpr (std::invocable<const FDeserialize&, reader&>)
1272 return std::type_identity<std::invoke_result_t<const FDeserialize&, reader&>>();
1273 else if constexpr (std::invocable<const FDeserialize&, deserialization_context&, const value&>)
1274 return std::type_identity<
1275 std::invoke_result_t<const FDeserialize&, deserialization_context&, const value&>>();
1276 else
1277 return std::type_identity<std::invoke_result_t<const FDeserialize&, const value&>>();
1278 }
1279
1280 using type = expected_value_or_self_t<std::remove_cvref_t<typename decltype(deduce())::type>>;
1281};
1282
1283template <typename FDeserialize>
1284using deserialize_function_result_t = typename deserialize_function_result<FDeserialize>::type;
1285
1286/// \c deserialize_entry for a \c T, which also owns the storage the \c T is built in.
1287template <typename T>
1289T deserialize_entry(deserialization_context& context, reader& from, source_lifetime lifetime)
1290{
1291 alignas(T) std::byte place[sizeof(T)];
1292 deserialize_entry(context,
1293 typeid(T),
1294 from,
1295 static_cast<void*>(place),
1296 [](void* p) noexcept { std::destroy_at(std::launder(static_cast<T*>(p))); },
1297 lifetime
1298 );
1299
1300 T* ptr = std::launder(reinterpret_cast<T*>(place));
1301 auto destroy = on_scope_exit([ptr] { std::destroy_at(ptr); });
1302 return std::move(*ptr);
1303}
1304
1305/// Deserialize a \c T from JSON \a source text through \a context.
1306///
1307/// A \c std::string rvalue is taken over: it is moved into a reader which lives as long as this call, so the
1308/// deserialization is told its source is temporary and refuses to hand back views of it. Anything else is read where it
1309/// is, through a
1310/// \c std::string_view, and views of it are the caller's to keep valid -- that includes an rvalue of any other string
1311/// type, such as a \c std::pmr::string, which outlives this call as every temporary argument does.
1312template <typename T, typename TSource>
1314T deserialize_text(TSource&& source, const parse_options& parse_opts, deserialization_context& context)
1315{
1316 if constexpr (std::is_same_v<TSource, std::string>)
1317 {
1318 reader from(std::forward<TSource>(source), parse_opts);
1319 return deserialize_entry<T>(context, from, source_lifetime::deserialization);
1320 }
1321 else
1322 {
1323 // Forwarded, so the conversion used is the one the constraint on the entry point accepted: a type may convert
1324 // to text only as an rvalue, or differently as an lvalue and as an rvalue.
1325 reader from(std::string_view(std::forward<TSource>(source)), parse_opts);
1326 return deserialize_entry<T>(context, from, source_lifetime::caller);
1327 }
1328}
1329
1330/// \c deserialize_text through a context of its own, built from \a fmts and \a options.
1331template <typename T, typename TSource>
1333T deserialize_text(TSource&& source,
1334 const parse_options& parse_opts,
1335 const formats& fmts,
1336 const deserialize_options& options
1337 )
1338{
1339 deserialization_context context(fmts, std::nullopt, jsonv::path(), nullptr, options);
1340 return deserialize_text<T>(std::forward<TSource>(source), parse_opts, context);
1341}
1342
1343}
1344
1345template <typename T>
1346T deserialization_context::deserialize(const value& from)
1347{
1348 reader rdr = reader::from_value(from);
1349 return detail::deserialize_entry<T>(*this, rdr, detail::source_lifetime::caller);
1350}
1351
1352/// Deserialize a C++ value from \a from using the provided \a fmts.
1353template <typename T>
1355T deserialize(const value& from, const formats& fmts)
1356{
1358 return context.deserialize<T>(from);
1359}
1360
1361/// Deserialize a C++ value from \a from using the provided \a fmts and \a options.
1362template <typename T>
1364T deserialize(const value& from, const formats& fmts, const deserialize_options& options)
1365{
1366 deserialization_context context(fmts, std::nullopt, jsonv::path(), nullptr, options);
1367 return context.deserialize<T>(from);
1368}
1369
1370/// Deserialize a C++ value from \a from using \c jsonv::formats::global().
1371template <typename T>
1373T deserialize(const value& from)
1374{
1376 return context.deserialize<T>(from);
1377}
1378
1379/// Deserialize a C++ value from \a from using \c jsonv::formats::global() and the provided \a options.
1380template <typename T>
1382T deserialize(const value& from, const deserialize_options& options)
1383{
1384 deserialization_context context(formats::global(), std::nullopt, jsonv::path(), nullptr, options);
1385 return context.deserialize<T>(from);
1386}
1387
1388/// \{
1389
1390/// Deserialize a C++ value from a \a reader using \a fmts (by default \c jsonv::formats::global()) and \a options.
1391///
1392/// A reader on \c ast_node_type::document_start -- a freshly-created one -- is read as a whole document. It is checked
1393/// with \c reader::validate first, so a source which did not parse is reported as that rather than as whatever a
1394/// deserializer made of the \c ast_node_type::error node it ran into; its \c document_start is stepped over, so neither
1395/// the caller nor any \c deserializer has to; and the value read must be the whole document, so the reader is left on
1396/// \c ast_node_type::document_end. A reader the caller has already positioned gets none of this: the value under its
1397/// cursor is deserialized and the cursor left one past it, as \c reader::next_value would, whatever surrounds it. The
1398/// exception is a reader on an \c ast_node_type::error node, which has no value to deserialize and is reported as the
1399/// parse failure it is.
1400///
1401/// Anything deserialized as a view of the source -- a \c std::string_view -- views the reader's storage. Through the
1402/// rvalue overloads, a reader which \c reader::owns_source dies with the call, so such views are refused; one over
1403/// storage the caller owns, like <tt>reader(std::string_view)</tt>, is viewed as usual.
1404///
1405/// \throws deserialization_error if the source did not parse, the value could not be deserialized or, for a whole
1406/// document, something follows the value.
1407template <typename T>
1410 const formats& fmts = formats::global(),
1411 const deserialize_options& options = deserialize_options()
1412 )
1413{
1414 deserialization_context context(fmts, std::nullopt, jsonv::path(), nullptr, options);
1415 return detail::deserialize_entry<T>(context, from, detail::source_lifetime::caller);
1416}
1417
1418/// Deserialize a C++ value from a \a reader using \c jsonv::formats::global() and the provided \a options.
1419template <typename T>
1422{
1423 return deserialize<T>(from, formats::global(), options);
1424}
1425
1426/// Deserialize a C++ value from a \a reader which may own its source, using \a fmts and \a options.
1427template <typename T>
1430 const formats& fmts = formats::global(),
1431 const deserialize_options& options = deserialize_options()
1432 )
1433{
1434 deserialization_context context(fmts, std::nullopt, jsonv::path(), nullptr, options);
1435 return detail::deserialize_entry<T>(context,
1436 from,
1437 from.owns_source() ? detail::source_lifetime::deserialization
1438 : detail::source_lifetime::caller
1439 );
1440}
1441
1442/// Deserialize a C++ value from a \a reader which may own its source, using \c jsonv::formats::global() and the
1443/// provided
1444/// \a options.
1445template <typename T>
1447T deserialize(reader&& from, const deserialize_options& options)
1448{
1449 return deserialize<T>(std::move(from), formats::global(), options);
1450}
1451/// \}
1452
1453/// \{
1454
1455/// Deserialize a C++ value from a \a reader through a \a context the caller built, exactly as the overloads above do
1456/// with one built from \c formats and \c deserialize_options.
1457///
1458/// Everything \a context was created with applies: its \c formats and \c deserialize_options, and also what the
1459/// overloads above have no way to be given -- the version, user data and base path its deserializers see, and the \c
1460/// deserialization_context::source_name its problems are reported in. A failed call throws the problems it recorded and
1461/// takes them off \a context, which is left holding what it held before.
1462///
1463/// A \c deserialization_context is single-use, so build one for each document and do not hand this the one a \c
1464/// deserializer was given -- whatever that deserialization has in progress would be applied to a document it knows
1465/// nothing about.
1466///
1467/// \throws deserialization_error for the same reasons as the overloads above.
1468template <typename T>
1471{
1472 return detail::deserialize_entry<T>(context, from, detail::source_lifetime::caller);
1473}
1474
1475template <typename T>
1478{
1479 return detail::deserialize_entry<T>(context,
1480 from,
1481 from.owns_source() ? detail::source_lifetime::deserialization
1482 : detail::source_lifetime::caller
1483 );
1484}
1485/// \}
1486
1487/// \{
1488
1489/// Deserialize a C++ value directly from JSON \a source text, parsed with \a parse_opts, using \a fmts (by default
1490/// \c jsonv::formats::global()) and \a options.
1491///
1492/// \a source is anything which converts to \c std::string_view: a string literal, a \c std::string, a
1493/// \c std::string_view. Note what that means for a C++ string: it is JSON text to be parsed, not a JSON string, so
1494/// <tt>deserialize<std::string>(R"("fire")")</tt> is \c "fire" and <tt>deserialize<std::string>("fire")</tt> is a parse
1495/// failure. Wrap it in a \c value -- <tt>deserialize<std::string>(value("fire"))</tt> -- to mean the string.
1496///
1497/// A \c std::string rvalue is taken over for the call and freed when it returns, so views of it are refused, as for a
1498/// tree materialised during deserialization (see \c deserialization_context::source_is_temporary). Every other source
1499/// is read where it is, without copying, and a \c std::string_view deserialized from it points into it.
1500///
1501/// This reads the whole document, exactly as the \c reader overloads do given a fresh \c reader.
1502///
1503/// \throws deserialization_error if \a source is not valid JSON, the value could not be deserialized, or something
1504/// follows it.
1505/// \throws std::invalid_argument if \a parse_opts asks for a \c parse_options::max_structure_depth beyond the limit,
1506/// as \c jsonv::parse does.
1507template <typename T, typename TSource>
1508 requires std::convertible_to<TSource, std::string_view>
1510T deserialize(TSource&& source,
1511 const formats& fmts = formats::global(),
1512 const deserialize_options& options = deserialize_options()
1513 )
1514{
1515 return detail::deserialize_text<T>(std::forward<TSource>(source), parse_options::create_default(), fmts, options);
1516}
1517
1518/// Deserialize a C++ value from JSON \a source text using \c jsonv::formats::global() and the provided \a options.
1519template <typename T, typename TSource>
1520 requires std::convertible_to<TSource, std::string_view>
1522T deserialize(TSource&& source, const deserialize_options& options)
1523{
1524 return detail::deserialize_text<T>(std::forward<TSource>(source),
1525 parse_options::create_default(),
1526 formats::global(),
1527 options
1528 );
1529}
1530
1531/// Deserialize a C++ value from JSON \a source text parsed with \a parse_opts, using \a fmts and \a options.
1532template <typename T, typename TSource>
1533 requires std::convertible_to<TSource, std::string_view>
1535T deserialize(TSource&& source,
1536 const parse_options& parse_opts,
1537 const formats& fmts = formats::global(),
1538 const deserialize_options& options = deserialize_options()
1539 )
1540{
1541 return detail::deserialize_text<T>(std::forward<TSource>(source), parse_opts, fmts, options);
1542}
1543
1544/// Deserialize a C++ value from JSON \a source text parsed with \a parse_opts, using \c jsonv::formats::global() and
1545/// the provided \a options.
1546template <typename T, typename TSource>
1547 requires std::convertible_to<TSource, std::string_view>
1549T deserialize(TSource&& source, const parse_options& parse_opts, const deserialize_options& options)
1550{
1551 return detail::deserialize_text<T>(std::forward<TSource>(source), parse_opts, formats::global(), options);
1552}
1553/// \}
1554
1555/// \{
1556
1557/// Deserialize a C++ value directly from JSON \a source text, parsed with \a parse_opts where they are given, through a
1558/// \a context the caller built.
1559///
1560/// \a source is read exactly as the overloads above read it, a \c std::string rvalue included, and \a context is used
1561/// as the \c reader overload taking a \c deserialization_context uses it: everything it was created with applies, and
1562/// the problems a failed call throws are taken off it. Build one for each document.
1563///
1564/// \throws deserialization_error if \a source is not valid JSON, the value could not be deserialized, or something
1565/// follows it.
1566/// \throws std::invalid_argument if \a parse_opts asks for a \c parse_options::max_structure_depth beyond the limit,
1567/// as \c jsonv::parse does.
1568template <typename T, typename TSource>
1569 requires std::convertible_to<TSource, std::string_view>
1572{
1573 return detail::deserialize_text<T>(std::forward<TSource>(source), parse_options::create_default(), context);
1574}
1575
1576template <typename T, typename TSource>
1577 requires std::convertible_to<TSource, std::string_view>
1579T deserialize(TSource&& source, const parse_options& parse_opts, deserialization_context& context)
1580{
1581 return detail::deserialize_text<T>(std::forward<TSource>(source), parse_opts, context);
1582}
1583/// \}
1584
1585/// \{
1586
1587/// Deserialize a C++ value from the JSON in what is left of the \a source stream, parsed with \a parse_opts, using
1588/// \a fmts (by default \c jsonv::formats::global()) and \a options. For example, to deserialize a file:
1589///
1590/// \code
1591/// std::ifstream file("file.json");
1592/// my_type out = jsonv::deserialize<my_type>(file);
1593/// \endcode
1594///
1595/// The stream is read to its end, as \c parse(std::istream&) reads it, into a \c reader(std::istream&) which lives as
1596/// long as this call. That text is then read exactly as the text overloads read a \c std::string rvalue: as a whole
1597/// document, with views of it refused, since it is freed when this returns. A \c parse_error position counts from
1598/// where \a source was when this was called.
1599///
1600/// \throws deserialization_error if what is left of \a source is not valid JSON, the value could not be deserialized,
1601/// or something follows it.
1602/// \throws std::invalid_argument if \a parse_opts asks for a \c parse_options::max_structure_depth beyond the limit,
1603/// as \c jsonv::parse does.
1604template <typename T>
1606T deserialize(std::istream& source,
1607 const formats& fmts = formats::global(),
1608 const deserialize_options& options = deserialize_options()
1609 )
1610{
1611 return deserialize<T>(reader(source, parse_options::create_default()), fmts, options);
1612}
1613
1614/// Deserialize a C++ value from the JSON in the \a source stream using \c jsonv::formats::global() and the provided
1615/// \a options.
1616template <typename T>
1618T deserialize(std::istream& source, const deserialize_options& options)
1619{
1620 return deserialize<T>(reader(source, parse_options::create_default()), formats::global(), options);
1621}
1622
1623/// Deserialize a C++ value from the JSON in the \a source stream parsed with \a parse_opts, using \a fmts and
1624/// \a options.
1625template <typename T>
1627T deserialize(std::istream& source,
1628 const parse_options& parse_opts,
1629 const formats& fmts = formats::global(),
1630 const deserialize_options& options = deserialize_options()
1631 )
1632{
1633 return deserialize<T>(reader(source, parse_opts), fmts, options);
1634}
1635
1636/// Deserialize a C++ value from the JSON in the \a source stream parsed with \a parse_opts, using
1637/// \c jsonv::formats::global() and the provided \a options.
1638template <typename T>
1640T deserialize(std::istream& source, const parse_options& parse_opts, const deserialize_options& options)
1641{
1642 return deserialize<T>(reader(source, parse_opts), formats::global(), options);
1643}
1644/// \}
1645
1646/// \{
1647
1648/// Deserialize a C++ value from the JSON in what is left of the \a source stream, parsed with \a parse_opts where they
1649/// are given, through a \a context the caller built.
1650///
1651/// \a source is read exactly as the overloads above read it, and \a context is used as the \c reader overload taking a
1652/// \c deserialization_context uses it: everything it was created with applies, and the problems a failed call throws
1653/// are taken off it. Build one for each document, and name the file it is read from as its
1654/// \c deserialization_context::source_name to have the problems say which file they are in.
1655///
1656/// \throws deserialization_error if what is left of \a source is not valid JSON, the value could not be deserialized,
1657/// or something follows it.
1658/// \throws std::invalid_argument if \a parse_opts asks for a \c parse_options::max_structure_depth beyond the limit,
1659/// as \c jsonv::parse does.
1660template <typename T>
1663{
1664 return deserialize<T>(reader(source, parse_options::create_default()), context);
1665}
1666
1667template <typename T>
1669T deserialize(std::istream& source, const parse_options& parse_opts, deserialization_context& context)
1670{
1671 return deserialize<T>(reader(source, parse_opts), context);
1672}
1673/// \}
1674
1675/// \}
1676
1677}
Utilities for directly dealing with a JSON AST.
std::string_view token_raw() const
Definition ast.hpp:187
The beginning of an kind::object ({).
Definition ast.hpp:255
std::string_view token_raw() const
Get a view of the raw token.
Definition ast.hpp:530
Provides extra information to routines used for deserialization and serialization.
Definition context.hpp:26
An RAII guard naming one step of the deserialization path while it is alive.
path_scope(deserialization_context &context, const TString &key) noexcept
Name the member key of the object being deserialized, on context.
path_scope(deserialization_context &context, path_element elem)
Name elem on context, keeping a copy of it for as long as this scope lives.
path_scope(deserialization_context &context, std::string_view key) noexcept
Name the member key of the object being deserialized, on context. key is viewed rather than copied.
path_scope(deserialization_context &context, TString &&key)
Name the member key of the object being deserialized, on context, keeping key for as long as this sco...
path_scope(deserialization_context &context, const char(&key)[N]) noexcept
Name the member key of the object being deserialized, on context.
path_scope(deserialization_context &context, std::size_t index) noexcept
Name the element at index of the array being deserialized, on context.
path_scope(deserialization_context &context, const char(&&key)[N])
Name the member key of the object being deserialized, on context, keeping a copy of key for as long a...
Provides extra information to routines used for deserialization, collects the problems they encounter...
jsonv::path problem_path(const reader &from) const
Where to report a problem noticed while from is sitting on the thing that is wrong.
jsonv::path path() const
Get the path currently being deserialized, as named by the live path_scope guards.
std::expected< void, ast_node_type > deserialize(const std::type_info &type, reader &from, void *into)
Attempt to deserialize an object of the given type from from into the memory at into,...
deserialization_error::problem_list problem_list
The problems recorded on a context, in the form a deserialization_error carries them.
std::string_view encoded_source() const
Get the JSON of the object a type described with the serialization builder DSL is being deserialized ...
const problem_list & problems() const &
Get the problems encountered so far. If this list is empty, no problems have occurred.
std::unexpected< ast_node_type > problem(TArgs &&... args)
Note that a problem has been encountered, forwarding args to a deserialization_error::problem.
const deserialize_options & options() const noexcept
Get the options this context is deserializing under.
deserialization_context(jsonv::formats fmt, std::optional< jsonv::version > ver=std::nullopt, jsonv::path p=jsonv::path(), const void *userdata=nullptr, deserialize_options options=deserialize_options(), std::string source_name=std::string())
Create a new instance using the given fmt, ver, p, userdata, options and source_name.
optional< const value & > source_value() const
Get the value a type described with the serialization builder DSL is being deserialized from,...
problem_list && problems() &&
Get the problems encountered so far. If this list is empty, no problems have occurred.
const std::string & source_name() const noexcept
Get the name of the document being deserialized, which every problem recorded here is reported in.
deserialization_context()
Create a new instance using the default formats (formats::global).
std::expected< T, ast_node_type > deserialize(reader &from)
Attempt to deserialize a T from from using the formats associated with this context.
bool recover() const noexcept
May deserialization recover from a failure and keep going?
Description of a single problem with deserialization.
const std::exception_ptr & nested_ptr() const noexcept
If there was an exception that caused this problem, extra details can be found in the nested exceptio...
problem(jsonv::path path, std::exception_ptr cause) noexcept
Create a problem with a message extracted from cause.
const std::string & source_name() const noexcept
The name of the document this problem was encountered in, such as the file it was read from.
problem(jsonv::path path, std::string message) noexcept
Create a problem for the given path, message, and optional cause.
const std::string & message() const noexcept
Human-readable details about the encountered problem.
problem(jsonv::path path, std::string message, std::exception_ptr cause) noexcept
Create a problem for the given path, message, and optional cause.
const jsonv::path & path() const noexcept
The path this problem was encountered at, within the document source_name names.
Exception thrown if there is any problem running deserialize.
deserialization_error(jsonv::path path, std::string message, std::exception_ptr cause) noexcept
Create a new deserialization_error with a single problem from the given path, message,...
deserialization_error(jsonv::path path, std::string message) noexcept
Create a new deserialization_error with a single problem from the given path, message,...
deserialization_error(problem_list problems) noexcept
Create a deserialization_error from the given list of problems.
deserialization_error(jsonv::path path, std::exception_ptr cause) noexcept
Create a new deserialization_error with a single problem at path, whose message is extracted from cau...
Configuration for various deserialization options. This becomes part of the deserialization_context.
deserialize_options() noexcept
Create an instance with the default options.
size_type max_failures() const
The number of problems to collect before giving up.
deserialize_options & failure_mode(on_error mode)
See on_error. The default failure mode is fail_immediately.
deserialize_options & max_failures(size_type limit)
The number of problems to collect before giving up.
on_error
When an error is encountered during deserialization, what should happen?
duplicate_key_action on_duplicate_key() const
See duplicate_key_action. The default action is replace.
duplicate_key_action
When an object key has the same value as a previously-seen key, what should happen?
deserialize_options & on_duplicate_key(duplicate_key_action action)
See duplicate_key_action. The default action is replace.
A deserializer holds the method for converting JSON source into an arbitrary C++ type.
virtual const std::type_info & get_type() const noexcept=0
Get the run-time type this deserializer knows how to deserialize.
Simply put, this class is a collection of deserializer and serializer instances.
Definition formats.hpp:157
Configuration for various parsing options.
Definition parse.hpp:58
Represents an exact path in some JSON structure.
Definition path.hpp:107
A reader instance reads from some form of JSON source (probably a string) and converts it into a JSON...
Definition reader.hpp:108
bool owns_source() const noexcept
Does this reader own the storage it reads from?
const ast_node & current() const
Get the current AST node this reader is pointing at.
optional< const value & > current_value() const noexcept
Get the in-memory value this reader is positioned on, if it has one to lend.
Represents a single JSON value, which can be any one of a potential kind, each behaving slightly diff...
Definition value.hpp:165
Copyright (c) 2014-2020 by Travis Gockel.
Copyright (c) 2015-2020 by Travis Gockel.
Copyright (c) 2012-2020 by Travis Gockel.
#define JSONV_NODISCARD
Warn if the caller discards the result of this function.
Definition config.hpp:143
#define JSONV_PUBLIC
This function or class is part of the public API for JSON Voorhees.
Definition config.hpp:124
T deserialize(const value &from, const formats &fmts)
Deserialize a C++ value from from using the provided fmts.
@ exception
A duplicate_type_error should be thrown.
@ ignore
The existing deserializer or serializer should be kept, but no exception should be thrown.
@ replace
The new deserializer or serializer should be inserted, and no exception should be thrown.
ast_node_type
Marker type for an encountered token type.
Definition ast.hpp:87
STL namespace.
Pulls in an implementation of optional.
typename detail::optional_type< T >::type optional
Represents a value that may or may not be present.
Definition optional.hpp:239
Copyright (c) 2012-2020 by Travis Gockel.
Parsed index of a JSON document.
Support for JSONPath.
Read a JSON AST.
Definition of the on_scope_exit utility.
Copyright (c) 2012-2020 by Travis Gockel.