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/string_view.hpp>
16
17#include <cstdint>
18#include <initializer_list>
19#include <memory>
20#include <string>
21
22namespace jsonv
23{
24
25class parse_index;
26class parse_options;
27class path;
28class value;
29
30/// \ingroup Serialization
31/// \{
32
33/// A reader instance reads from some form of JSON source (probably a string) and converts it into a JSON \ref ast_node
34/// sequence.
35///
36/// Readers normalize access to JSON source for conversion to some other format. They can be provided with pre-parsed
37/// JSON through a \c parse_index or \c value. They can be provided with a \c std::string or \c string_view directly.
38/// This allows \c extractor implementations to operate on all forms of JSON without worrying about the implementation.
40{
41public:
42 /// Create a reader which reads from the given \a index.
43 explicit reader(parse_index index);
44
45 /// Create a reader which reads from an in-memory \a value.
46 ///
47 /// \param value The value to read from. This must remain valid for the lifetime of the reader.
48 explicit reader(const value* value);
49
50 /// \{
51 /// Create a reader which reads from JSON \a source.
52 ///
53 /// \param source The JSON source code to parse from. This must stay in memory for the duration of this instance's
54 /// use. If source is an rvalue reference to a \c std::string instance, the \a source is moved to the
55 /// reader's implementation to keep alive.
56 /// \param parse_options If specified, use these options to parse \a source. If unspecified, use the default values
57 /// of \a parse_options for \c parse_index::parse.
60 explicit reader(const char* source);
61 explicit reader(const char* source, const parse_options& parse_options);
62
63 explicit reader(std::string&& source);
64 explicit reader(std::string&& source, const parse_options& parse_options);
65 /// \}
66
67 // Not copyable.
68 reader(const reader&) = delete;
69 reader& operator=(const reader&) = delete;
70
73
75
76 /// Check if this reader is still good to read from. This will be \c true if this instance has not been moved-from
77 /// and has not reached EOF. If this is \c false, \c current or \c current_path will throw an exception.
78 bool good() const;
79
80 /// Get the current AST node this reader is pointing at.
81 ///
82 /// \throws std::invalid_argument if this instance is not \c good.
83 const ast_node& current() const;
84
85 /// \{
86 /// Check that the \c current AST node has the given \a type or is one of the expected \a types. Using this leads to
87 /// a slightly more informative error message than `current.as<T>()` call.
88 ///
89 /// \throws extraction_error if the current node does not match the expected \a type or \a types.
90 /// \throws std::invalid_argument if this instance is not \c good.
91 void expect(ast_node_type type);
93 /// \}
94
95 /// Get the \c current AST node as a specific \c TAstNode subtype, calling \c expect beforehand.
96 ///
97 /// \throws std::invalid_argument if this instance is not \c good.
98 /// \throws extraction_error if the current node does not match `TAstNode::type()`.
100 TAstNode current_as() const
101 {
102 expect(TAstNode::type());
103 return current().as<TAstNode>();
104 }
105
106 /// Get the path to the current node this reader is pointing at. This is used in the generation of error messages to
107 /// describe the location of something that could not be extracted.
108 ///
109 /// \code
110 /// ^ /* "." -- start of document is the empty path */
111 /// { /* "." -- opening { is still an empty path */
112 /// "a": /* ".a" -- the key starts the path */
113 /// [ /* ".a" -- the path refers to the entire array */
114 /// 1, /* ".a[0]" */
115 /// 2, /* ".a[1]" */
116 /// 3, /* ".a[2]" */
117 /// ], /* ".a" -- the path at the end of the array refers to the entire array again */
118 /// "b": /* ".b" */
119 /// { /* ".b" -- the path refers to the entire object */
120 /// "x": /* ".b.x" */
121 /// "taco" /* ".b.x" */
122 /// }, /* ".b" */
123 /// "c": /* ".c" */
124 /// 4 /* ".c" */
125 /// } /* "." */
126 /// $ /* "." */
127 /// \endcode
128 ///
129 /// \throws std::invalid_argument if this instance is not \c good.
130 const path& current_path() const;
131
132 /// Go to the next token.
133 ///
134 /// \code
135 /// ^
136 /// { /* <- go to "a" */
137 /// "a": /* <- go to [ */
138 /// [ /* <- go to 1 */
139 /// 1, /* <- go to 2 */
140 /// 2, /* ...and so on */
141 /// 3,
142 /// ],
143 /// "b":
144 /// {
145 /// },
146 /// "c":
147 /// 4
148 /// }
149 /// $
150 /// \endcode
151 ///
152 /// \returns \c true if the reader is still \c good to read from \c current.
153 [[nodiscard]]
155
156 /// Go to one past the end of the current structure.
157 ///
158 /// \code
159 /// ^
160 /// {
161 /// "a": /* <- go to end of document */
162 /// [ /* <- go to "b" */
163 /// 1, /* <- go to "b", too */
164 /// 2,
165 /// 3,
166 /// ], /* <- go to "b" */
167 /// "b": /* <- go to end of document */
168 /// { /* <- go to "c" */
169 /// }, /* <- go to "c" */
170 /// "c":
171 /// 4
172 /// } /* <- go to end of document */
173 /// $
174 /// \endcode
175 ///
176 /// This is meant to be used when parsing an object or array
177 ///
178 /// \code
179 /// my_object extract_my_object(jsonv::reader& from)
180 /// {
181 /// from.expect(jsonv::ast_node_type::object_begin);
182 /// from.next_token();
183 ///
184 /// // Use this helper macro to always execute code
185 /// JSONV_SCOPE_EXIT { from.next_structure(); };
186 ///
187 /// std::optional<std::int64_t> a;
188 /// while (from.good())
189 /// {
190 /// auto token = from.current();
191 /// if (token.type() == jsonv::ast_node_type::object_end)
192 /// {
193 /// return my_object(a.value_or(0));
194 /// }
195 /// else if (token.type() == jsonv::ast_node_type::key_canonical)
196 /// {
197 /// if (token.value() == "a")
198 /// {
199 /// reader.next_token();
200 /// a = reader.current_as<jsonv::ast_node::integer>().value();
201 /// reader.next_token();
202 /// }
203 /// else
204 /// {
205 /// // ignore
206 /// reader.next_key();
207 /// }
208 /// }
209 /// else
210 /// {
211 /// throw jsonv::extraction_error(from.current_path(), "Did not handle escaped keys");
212 /// }
213 /// }
214 /// }
215 /// \endcode
216 ///
217 /// \returns \c true if the reader is still \c good to read from \c current.
218 [[nodiscard]]
219 bool next_structure() noexcept;
220
221 /// Go to the next object key or end-of-object.
222 ///
223 /// \code
224 /// ^
225 /// {
226 /// "a": /* <- calling here goes to "b" */
227 /// [ /* <- calling when not on an object key throws */
228 /// 1,
229 /// 2,
230 /// 3,
231 /// ],
232 /// "b": /* <- calling here goes to "c" */
233 /// {
234 /// },
235 /// "c": /* <- calling here goes to end of object */
236 /// 4
237 /// }
238 /// $
239 /// \endcode
240 ///
241 /// \returns \c true if the reader is still \c good to read from \c current.
242 /// \throws std::invalid_argument if the reader is not currently at the start of a key.
243 [[nodiscard]]
244 bool next_key() noexcept;
245
246private:
247 class impl;
251
254
255private:
256 std::unique_ptr<impl> _impl;
257};
258
259/// \}
260
261}
Utilities for directly dealing with a JSON AST.
Represents an entry in a JSON AST.
Definition ast.hpp:160
An adapter for enumeration types.
Represents the index of a parsed AST.
Configuration for various parsing options.
Definition parse.hpp:63
Represents an exact path in some JSON structure.
Definition path.hpp:83
A reader instance reads from some form of JSON source (probably a string) and converts it into a JSON...
Definition reader.hpp:40
reader(const value *value)
Create a reader which reads from an in-memory value.
bool next_token() noexcept
Go to the next token.
reader(string_view source)
reader(parse_index index)
Create a reader which reads from the given index.
const path & current_path() const
Get the path to the current node this reader is pointing at.
Represents a single JSON value, which can be any one of a potential kind, each behaving slightly diff...
Definition value.hpp:107
Copyright (c) 2014-2020 by Travis Gockel.
#define JSONV_PUBLIC
This function or class is part of the public API for JSON Voorhees.
Definition config.hpp:102
ast_node_type
Marker type for an encountered token type.
Definition ast.hpp:84
STL namespace.
Pulls in an implementation of string_view.
std::string_view string_view
A non-owning reference to a string.