JSON Voorhees
Killer JSON for C++
Loading...
Searching...
No Matches
reader.hpp
Go to the documentation of this file.
1/// \file jsonv/reader.hpp
2/// Read a JSON AST.
3///
4/// Copyright (c) 2015-2022 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>
15#include <jsonv/optional.hpp>
16#include <expected>
17#include <string_view>
18
19#include <cstdint>
20#include <initializer_list>
21#include <iosfwd>
22#include <memory>
23#include <string>
24
25namespace jsonv
26{
27
28class parse_index;
29class parse_options;
30class path;
31class value;
32
33namespace detail
34{
35
36class reader_lookahead;
37
38}
39
40/// \addtogroup Serialization
41/// \{
42
43/// A reader instance reads from some form of JSON source (probably a string) and converts it into a JSON \ref ast_node
44/// sequence.
45///
46/// Readers normalize access to JSON source for conversion to some other format. They can be provided with pre-parsed
47/// JSON through a \c parse_index or \c value. They can be provided with a \c std::string or \c std::string_view
48/// directly. This allows \c deserializer implementations to operate on all forms of JSON without worrying about the
49/// implementation.
50///
51/// A reader is a forward cursor over that sequence. It starts on \c ast_node_type::document_start, so the first thing
52/// to do is step onto the value itself -- which \c jsonv::deserialize does for you when handed a fresh reader. Reading
53/// an object means walking its keys, handling the ones you recognize and skipping the ones you do not:
54///
55/// \code
56/// struct my_object
57/// {
58/// std::int64_t a = 0;
59/// };
60///
61/// std::optional<my_object> deserialize_my_object(jsonv::reader& from)
62/// {
63/// if (!from.expect(jsonv::ast_node_type::object_begin))
64/// return std::nullopt;
65///
66/// // Step onto the first key, or onto the } of an empty object.
67/// if (!from.next_token())
68/// return std::nullopt;
69///
70/// my_object out;
71/// while (from.good() && from.current_type() != jsonv::ast_node_type::object_end)
72/// {
73/// // Keys arrive canonical or escaped, depending on whether the source used escape sequences.
74/// if (!from.expect({ jsonv::ast_node_type::key_canonical, jsonv::ast_node_type::key_escaped }))
75/// return std::nullopt;
76///
77/// auto key = from.current().visit_key([](const auto& k) { return std::string(k.value()); });
78/// if (key == "a")
79/// {
80/// if (!from.next_token())
81/// return std::nullopt;
82///
83/// if (auto node = from.current_as<jsonv::ast_node::integer>())
84/// out.a = node->value();
85/// else
86/// return std::nullopt;
87///
88/// // Step off the value and onto the next key, or onto the closing }.
89/// if (!from.next_token())
90/// return std::nullopt;
91/// }
92/// else
93/// {
94/// // A key we do not care about -- skip its value, however large, and land on the next key.
95/// if (!from.next_key())
96/// return std::nullopt;
97/// }
98/// }
99/// return out;
100/// }
101/// \endcode
102///
103/// Note that \c next_key is only valid while sitting on a key or on the opening \c { -- it is the "skip this member"
104/// step, not the loop's advance. Use \c next_token to move off a value you have just read.
106{
107public:
108 /// Create a reader which reads from the given \a index.
109 explicit reader(parse_index index);
110
111 /// \{
112
113 /// Create a reader which reads from an in-memory \a value.
114 ///
115 /// \param value The value to read from. The overload taking a reference does not copy it, so it must remain
116 /// valid for the lifetime of the reader; the rvalue overload moves \a value into the reader, which
117 /// then keeps it alive.
118 ///
119 /// These are named rather than constructors because \c value converts implicitly from \c std::string, among
120 /// others. A \c reader(const value&) constructor would make <tt>reader(some_std_string)</tt> ambiguous -- a
121 /// \c std::string reaches \c std::string_view and \c value through user-defined conversions of equal rank --
122 /// and the same trap would reopen for every type \c value grows a converting constructor from. Returning by
123 /// value costs nothing: the result is a prvalue of the returned type, so it initializes the caller's object
124 /// directly without a move.
126 static reader from_value(const value& value);
129 /// \}
130
131 /// \{
132
133 /// Create a reader which reads from JSON \a source.
134 ///
135 /// The \a source must stay in memory for the duration of this instance's use, unless it is an rvalue reference to
136 /// a \c std::string, which is moved to the reader's implementation to keep it alive. It is parsed with
137 /// \a parse_options where they are given, and with the defaults \c parse_index::parse uses where they are not.
138 explicit reader(std::string_view source);
139 explicit reader(std::string_view source, const parse_options& parse_options);
140 explicit reader(const char* source);
141 explicit reader(const char* source, const parse_options& parse_options);
142
143 explicit reader(std::string&& source);
144 explicit reader(std::string&& source, const parse_options& parse_options);
145 /// \}
146
147 /// \{
148
149 /// Create a reader which reads JSON from what is left of the \a source stream, as \c parse(std::istream&) does.
150 ///
151 /// \code
152 /// std::ifstream file("file.json");
153 /// jsonv::reader from(file);
154 /// \endcode
155 ///
156 /// The stream is read to its end here, and the reader keeps the text, so \c owns_source is \c true and nothing
157 /// reads \a source again once this returns. The text is parsed with \a parse_options where they are given, and
158 /// with the defaults \c parse_index::parse uses where they are not. A \c parse_error position counts from where
159 /// \a source was when this was called.
160 explicit reader(std::istream& source);
161 explicit reader(std::istream& source, const parse_options& parse_options);
162 /// \}
163
164 // Not copyable.
165 reader(const reader&) = delete;
166 reader& operator=(const reader&) = delete;
167
168 /// \{
169
170 /// Moving a reader transfers its implementation, leaving the source moved-from: \c good is \c false and the
171 /// accessors throw \c std::invalid_argument. These are out-of-line because destroying the implementation needs a
172 /// complete \c reader::impl, which this header does not have -- the same reason the destructor is.
173 reader(reader&&) noexcept;
174 reader& operator=(reader&&) noexcept;
175 /// \}
176
177 ~reader() noexcept;
178
179 /// Check if this reader is still good to read from. This will be \c true if this instance has not been moved-from
180 /// and has not reached EOF. If this is \c false, \c current or \c current_path will throw an exception.
182 bool good() const;
183
184 /// Check that the source this reader was created from is valid JSON.
185 ///
186 /// Parsing text never throws: a malformed document produces a sequence which stops at an \c ast_node_type::error
187 /// node, and a reader walks it as far as it goes. What was wrong is not on that node in any form a reader of it can
188 /// act on, so this is the question \c parse_index::validate answers, asked of the reader. It is about the source,
189 /// not the cursor, so the answer is the same wherever the reader is positioned.
190 ///
191 /// \throws parse_error if this reader is over JSON text which did not parse. A reader over a \c value has nothing
192 /// to parse and never throws this.
193 /// \throws std::invalid_argument if this instance has been moved-from.
194 void validate() const;
195
196 /// Does this reader own the storage it reads from?
197 ///
198 /// This is \c true for a reader created from a \c std::string rvalue or by \c from_value(value&&), which keep their
199 /// source alive for exactly as long as the reader. Anything deserialized as a view of the source -- a
200 /// \c std::string_view, say -- is then valid only while the reader is. It is \c false for every other source,
201 /// where the caller owns the storage, and for a moved-from reader.
203 bool owns_source() const noexcept;
204
205 /// Get the current AST node this reader is pointing at.
206 ///
207 /// \throws std::logic_error if this instance is not \c good, or std::invalid_argument if it has been moved-from.
209 const ast_node& current() const;
210
211 /// Get the type of the \c current AST node, which is always \c current().type().
212 ///
213 /// Use this where the type is all that is wanted -- checking for the `]` which ends an array, say. A reader over a
214 /// \c value has to synthesise token text for a number, a string or a key before it can hand out an \c ast_node,
215 /// and answering this does not.
216 ///
217 /// \throws std::logic_error if this instance is not \c good, or std::invalid_argument if it has been moved-from.
219 ast_node_type current_type() const;
220
221 /// \{
222
223 /// Check that the \c current AST node has the given \a type or is one of the expected \a types.
224 ///
225 /// \returns Nothing if the \c current node matches \a type or one of the given \a types; otherwise the
226 /// \c ast_node_type the \c current node actually has.
227 /// \throws std::invalid_argument if \a types is empty.
228 /// \throws std::logic_error if this instance is not \c good, or std::invalid_argument if it has been moved-from.
229 ///
230 /// \see ast_node::expect
232 std::expected<void, ast_node_type> expect(ast_node_type type) const;
234 std::expected<void, ast_node_type> expect(std::initializer_list<ast_node_type> types) const;
235 /// \}
236
237 /// Get the \c current AST node as a specific \c TAstNode subtype, calling \c expect beforehand.
238 ///
239 /// \returns The \c current node as a \c TAstNode; otherwise the \c ast_node_type the \c current node actually has.
240 /// \throws std::logic_error if this instance is not \c good, or std::invalid_argument if it has been moved-from.
241 template <typename TAstNode>
243 std::expected<TAstNode, ast_node_type> current_as() const
244 {
245 // Written as an explicit branch rather than `expect(...).transform(...)` on purpose. The monadic operations on
246 // `std::expected` are a later addition than the type itself -- libstdc++ 12 and libc++ 16 have `expected` but
247 // no `transform` -- so using one here would quietly raise the minimum toolchain by a whole release.
248 if (auto matched = expect(TAstNode::type()); !matched)
249 return std::unexpected(matched.error());
250 else
251 return current().as<TAstNode>();
252 }
253
254 /// Get the in-memory \c value this reader is positioned on, if it has one to lend.
255 ///
256 /// A reader created by \c from_value is walking a \c value which already exists, so the subtree under \c current
257 /// is something it can hand out by reference rather than rebuild. A reader over JSON text has no such tree.
258 ///
259 /// Deserialization uses this to avoid copying a subtree it was already given, and so that a deserializer which
260 /// returns a view of what it was handed -- \c std::string_view among them -- borrows the caller's storage rather
261 /// than a temporary which dies with the call.
262 ///
263 /// \returns The value \c current names; or nothing if this reader is not value-backed, is not \c good, or is
264 /// positioned somewhere which does not start a value, such as an object key or a closing token.
267
268 /// Get the path to the current node this reader is pointing at. This is used in the generation of error messages to
269 /// describe the location of something that could not be deserialized.
270 ///
271 /// \code
272 /// ^ /* "." -- start of document is the empty path */
273 /// { /* "." -- opening { is still an empty path */
274 /// "a": /* ".a" -- the key starts the path */
275 /// [ /* ".a" -- the path refers to the entire array */
276 /// 1, /* ".a[0]" */
277 /// 2, /* ".a[1]" */
278 /// 3, /* ".a[2]" */
279 /// ], /* ".a" -- the path at the end of the array refers to the entire array again */
280 /// "b": /* ".b" */
281 /// { /* ".b" -- the path refers to the entire object */
282 /// "x": /* ".b.x" */
283 /// "taco" /* ".b.x" */
284 /// }, /* ".b" */
285 /// "c": /* ".c" */
286 /// 4 /* ".c" */
287 /// } /* "." */
288 /// $ /* "." */
289 /// \endcode
290 ///
291 /// \throws std::logic_error if this instance is not \c good, or std::invalid_argument if it has been moved-from.
293 const path& current_path() const;
294
295 /// Go to the next token.
296 ///
297 /// \code
298 /// ^
299 /// { /* <- go to "a" */
300 /// "a": /* <- go to [ */
301 /// [ /* <- go to 1 */
302 /// 1, /* <- go to 2 */
303 /// 2, /* ...and so on */
304 /// 3,
305 /// ],
306 /// "b":
307 /// {
308 /// },
309 /// "c":
310 /// 4
311 /// }
312 /// $
313 /// \endcode
314 ///
315 /// \returns \c true if the reader is still \c good to read from \c current.
317 bool next_token() noexcept;
318
319 /// Go to one past the end of the current structure.
320 ///
321 /// \code
322 /// ^
323 /// {
324 /// "a": /* <- go to end of document */
325 /// [ /* <- go to "b" */
326 /// 1, /* <- go to "b", too */
327 /// 2,
328 /// 3,
329 /// ], /* <- go to "b" */
330 /// "b": /* <- go to end of document */
331 /// { /* <- go to "c" */
332 /// }, /* <- go to "c" */
333 /// "c":
334 /// 4
335 /// } /* <- go to end of document */
336 /// $
337 /// \endcode
338 ///
339 /// The point of this over \ref next_token is abandoning a structure you are only part-way through. Once the one
340 /// member you came for has been read, there is no reason to walk the rest of the object:
341 ///
342 /// \code
343 /// /// Find the "a" member of an object and leave the rest of it unread.
344 /// std::optional<std::int64_t> find_a(jsonv::reader& from)
345 /// {
346 /// if (!from.expect(jsonv::ast_node_type::object_begin))
347 /// return std::nullopt;
348 ///
349 /// if (!from.next_token())
350 /// return std::nullopt;
351 ///
352 /// while (from.good() && from.current_type() != jsonv::ast_node_type::object_end)
353 /// {
354 /// if (!from.expect({ jsonv::ast_node_type::key_canonical, jsonv::ast_node_type::key_escaped }))
355 /// return std::nullopt;
356 ///
357 /// auto key = from.current().visit_key([](const auto& k) { return std::string(k.value()); });
358 /// if (key != "a")
359 /// {
360 /// if (!from.next_key())
361 /// return std::nullopt;
362 ///
363 /// continue;
364 /// }
365 ///
366 /// if (!from.next_token())
367 /// return std::nullopt;
368 ///
369 /// auto node = from.current_as<jsonv::ast_node::integer>();
370 ///
371 /// // Whatever is left of this object, we are done with it.
372 /// (void) from.next_structure();
373 ///
374 /// if (node)
375 /// return node->value();
376 /// else
377 /// return std::nullopt;
378 /// }
379 /// return std::nullopt;
380 /// }
381 /// \endcode
382 ///
383 /// Note the call site: \c next_structure is used while sitting on a *value* inside the object, which is where it
384 /// differs from \ref next_token. Called on the closing \c } itself it is merely \ref next_token, since there is no
385 /// longer a structure to leave.
386 ///
387 /// \returns \c true if the reader is still \c good to read from \c current.
389 bool next_structure() noexcept;
390
391 /// Go to one past the value this reader is on.
392 ///
393 /// Unlike \ref next_structure, which leaves the structure the reader is *inside*, this steps over the single value
394 /// the reader is *on*. On a structure that means the token *after* its matching close, since the structure is the
395 /// value being stepped over; on anything else it is the same as \ref next_token.
396 ///
397 /// \code
398 /// ^
399 /// {
400 /// "a": /* <- go to [ */
401 /// [ /* <- go to "b" */
402 /// 1, /* <- go to 2 */
403 /// 2,
404 /// 3,
405 /// ],
406 /// "b":
407 /// { /* <- go to "c" */
408 /// },
409 /// "c":
410 /// 4 /* <- go to } */
411 /// }
412 /// $
413 /// \endcode
414 ///
415 /// This is the primitive for ignoring a value you do not want. Note the difference from \ref next_structure at the
416 /// `4` above: this goes to the `}`, while \ref next_structure leaves the enclosing object entirely.
417 ///
418 /// \returns \c true if the reader is still \c good to read from \c current.
420 bool next_value() noexcept;
421
422 /// Go to the next object key or end-of-object.
423 ///
424 /// \code
425 /// ^
426 /// {
427 /// "a": /* <- calling here goes to "b" */
428 /// [ /* <- calling when not on an object key throws */
429 /// 1,
430 /// 2,
431 /// 3,
432 /// ],
433 /// "b": /* <- calling here goes to "c" */
434 /// {
435 /// },
436 /// "c": /* <- calling here goes to end of object */
437 /// 4
438 /// }
439 /// $
440 /// \endcode
441 ///
442 /// \returns \c true if the reader is still \c good to read from \c current.
443 /// \throws std::invalid_argument if the reader is not currently at the start of a key.
445 bool next_key();
446
447private:
448 class impl;
449 class impl_parse_index;
450 class impl_parse_index_owning;
451 class impl_value;
452 class impl_value_owning;
453
454 template <typename TImpl, typename... TArgs>
455 explicit reader(std::in_place_type_t<TImpl>, TArgs&&...);
456
457 explicit reader(std::unique_ptr<impl> impl) noexcept;
458
459 /// The library sometimes has to read ahead -- \c polymorphic_adapter has to find its discriminator before it knows
460 /// which type to deserialize. That is a second cursor on this reader's source rather than a rewind of this one, and
461 /// it is kept out of the public interface on purpose: promising it would commit every future source, including one
462 /// which streams, to supporting it.
463 friend class detail::reader_lookahead;
464
465private:
466 std::unique_ptr<impl> _impl;
467};
468
469/// \}
470
471}
Utilities for directly dealing with a JSON AST.
Represents an entry in a JSON AST.
Definition ast.hpp:172
Represents the index of a parsed AST.
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:106
static reader from_value(const value &value)
Create a reader which reads from an in-memory value.
reader(std::string_view source, const parse_options &parse_options)
Create a reader which reads from JSON source.
reader(std::string &&source, const parse_options &parse_options)
Create a reader which reads from JSON source.
reader(std::istream &source)
Create a reader which reads JSON from what is left of the source stream, as parse(std::istream&) does...
reader(const char *source, const parse_options &parse_options)
Create a reader which reads from JSON source.
reader(parse_index index)
Create a reader which reads from the given index.
reader(std::string &&source)
Create a reader which reads from JSON source.
reader(std::istream &source, const parse_options &parse_options)
Create a reader which reads JSON from what is left of the source stream, as parse(std::istream&) does...
reader(std::string_view source)
Create a reader which reads from JSON source.
static reader from_value(value &&value)
Create a reader which reads from an in-memory value.
reader(const char *source)
Create a reader which reads from JSON source.
optional< const value & > current_value() const noexcept
Get the in-memory value this reader is positioned on, if it has one to lend.
reader(reader &&) noexcept
Moving a reader transfers its implementation, leaving the source moved-from: good is false and the ac...
Represents a single JSON value, which can be any one of a potential kind, each behaving slightly diff...
Definition value.hpp:156
Copyright (c) 2014-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
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