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