1#include "simdjson/error.h"
2
3namespace simdjson {
4namespace SIMDJSON_IMPLEMENTATION {
5namespace ondemand {
6
7/**
8 * A forward-only JSON object field iterator.
9 */
10class object {
11public:
12 /**
13 * Create a new invalid object.
14 *
15 * Exists so you can declare a variable and later assign to it before use.
16 */
17 simdjson_inline object() noexcept = default;
18
19 simdjson_inline simdjson_result<object_iterator> begin() noexcept;
20 simdjson_inline simdjson_result<object_iterator> end() noexcept;
21 /**
22 * Look up a field by name on an object (order-sensitive).
23 *
24 * The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
25 * JSON `{ "x": 1, "y": 2, "z": 3 }`:
26 *
27 * ```c++
28 * simdjson::ondemand::parser parser;
29 * auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
30 * double z = obj.find_field("z");
31 * double y = obj.find_field("y");
32 * double x = obj.find_field("x");
33 * ```
34 * If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
35 * that only one field is returned.
36 *
37 * **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
38 * e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
39 *
40 * You must consume the fields on an object one at a time. A request for a new key
41 * invalidates previous field values: it makes them unsafe. The value instance you get
42 * from `content["bids"]` becomes invalid when you call `content["asks"]`. The array
43 * given by content["bids"].get_array() should not be accessed after you have called
44 * content["asks"].get_array(). You can detect such mistakes by first compiling and running
45 * the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
46 * OUT_OF_ORDER_ITERATION error is generated.
47 *
48 * You are expected to access keys only once. You should access the value corresponding to a
49 * key a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string()
50 * is an error.
51 *
52 * @param key The key to look up.
53 * @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
54 */
55 simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
56 /** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept; */
57 simdjson_inline simdjson_result<value> find_field(std::string_view key) && noexcept;
58
59 /**
60 * Look up a field by name on an object, without regard to key order.
61 *
62 * **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
63 * and often appears negligible. It starts out normally, starting out at the last field; but if
64 * the field is not found, it scans from the beginning of the object to see if it missed it. That
65 * missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
66 * in question is large. The fact that the extra code is there also bumps the executable size.
67 *
68 * It is the default, however, because it would be highly surprising (and hard to debug) if the
69 * default behavior failed to look up a field just because it was in the wrong order--and many
70 * APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
71 *
72 * Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
73 * field wasn't there when they aren't).
74 *
75 * If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
76 * that only one field is returned.
77 *
78 * You must consume the fields on an object one at a time. A request for a new key
79 * invalidates previous field values: it makes them unsafe. The value instance you get
80 * from `content["bids"]` becomes invalid when you call `content["asks"]`. The array
81 * given by content["bids"].get_array() should not be accessed after you have called
82 * content["asks"].get_array(). You can detect such mistakes by first compiling and running
83 * the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
84 * OUT_OF_ORDER_ITERATION error is generated.
85 *
86 * You are expected to access keys only once. You should access the value corresponding to a key
87 * a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string() is an error.
88 *
89 * @param key The key to look up.
90 * @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
91 */
92 simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
93 /** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
94 simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) && noexcept;
95 /** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
96 simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
97 /** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
98 simdjson_inline simdjson_result<value> operator[](std::string_view key) && noexcept;
99
100 /**
101 * Get the value associated with the given JSON pointer. We use the RFC 6901
102 * https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
103 * as the root of its own JSON document.
104 *
105 * ondemand::parser parser;
106 * auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
107 * auto doc = parser.iterate(json);
108 * doc.at_pointer("/foo/a/1") == 20
109 *
110 * It is allowed for a key to be the empty string:
111 *
112 * ondemand::parser parser;
113 * auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
114 * auto doc = parser.iterate(json);
115 * doc.at_pointer("//a/1") == 20
116 *
117 * Note that at_pointer() called on the document automatically calls the document's rewind
118 * method between each call. It invalidates all previously accessed arrays, objects and values
119 * that have not been consumed. Yet it is not the case when calling at_pointer on an object
120 * instance: there is no rewind and no invalidation.
121 *
122 * You may call at_pointer more than once on an object, but each time the pointer is advanced
123 * to be within the value matched by the key indicated by the JSON pointer query. Thus any preceding
124 * key (as well as the current key) can no longer be used with following JSON pointer calls.
125 *
126 * Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching.
127 *
128 * @return The value associated with the given JSON pointer, or:
129 * - NO_SUCH_FIELD if a field does not exist in an object
130 * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
131 * - INCORRECT_TYPE if a non-integer is used to access an array
132 * - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
133 */
134 inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
135
136 /**
137 * Reset the iterator so that we are pointing back at the
138 * beginning of the object. You should still consume values only once even if you
139 * can iterate through the object more than once. If you unescape a string within
140 * the object more than once, you have unsafe code. Note that rewinding an object
141 * means that you may need to reparse it anew: it is not a free operation.
142 *
143 * @returns true if the object contains some elements (not empty)
144 */
145 inline simdjson_result<bool> reset() & noexcept;
146 /**
147 * This method scans the beginning of the object and checks whether the
148 * object is empty.
149 * The runtime complexity is constant time. After
150 * calling this function, if successful, the object is 'rewinded' at its
151 * beginning as if it had never been accessed. If the JSON is malformed (e.g.,
152 * there is a missing comma), then an error is returned and it is no longer
153 * safe to continue.
154 */
155 inline simdjson_result<bool> is_empty() & noexcept;
156 /**
157 * This method scans the object and counts the number of key-value pairs.
158 * The count_fields method should always be called before you have begun
159 * iterating through the object: it is expected that you are pointing at
160 * the beginning of the object.
161 * The runtime complexity is linear in the size of the object. After
162 * calling this function, if successful, the object is 'rewinded' at its
163 * beginning as if it had never been accessed. If the JSON is malformed (e.g.,
164 * there is a missing comma), then an error is returned and it is no longer
165 * safe to continue.
166 *
167 * To check that an object is empty, it is more performant to use
168 * the is_empty() method.
169 *
170 * Performance hint: You should only call count_fields() as a last
171 * resort as it may require scanning the document twice or more.
172 */
173 simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
174 /**
175 * Consumes the object and returns a string_view instance corresponding to the
176 * object as represented in JSON. It points inside the original byte array containing
177 * the JSON document.
178 */
179 simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
180
181protected:
182 /**
183 * Go to the end of the object, no matter where you are right now.
184 */
185 simdjson_inline error_code consume() noexcept;
186 static simdjson_inline simdjson_result<object> start(value_iterator &iter) noexcept;
187 static simdjson_inline simdjson_result<object> start_root(value_iterator &iter) noexcept;
188 static simdjson_inline simdjson_result<object> started(value_iterator &iter) noexcept;
189 static simdjson_inline object resume(const value_iterator &iter) noexcept;
190 simdjson_inline object(const value_iterator &iter) noexcept;
191
192 simdjson_warn_unused simdjson_inline error_code find_field_raw(const std::string_view key) noexcept;
193
194 value_iterator iter{};
195
196 friend class value;
197 friend class document;
198 friend struct simdjson_result<object>;
199};
200
201} // namespace ondemand
202} // namespace SIMDJSON_IMPLEMENTATION
203} // namespace simdjson
204
205namespace simdjson {
206
207template<>
208struct simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object> : public SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::object> {
209public:
210 simdjson_inline simdjson_result(SIMDJSON_IMPLEMENTATION::ondemand::object &&value) noexcept; ///< @private
211 simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
212 simdjson_inline simdjson_result() noexcept = default;
213
214 simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object_iterator> begin() noexcept;
215 simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object_iterator> end() noexcept;
216 simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field(std::string_view key) & noexcept;
217 simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field(std::string_view key) && noexcept;
218 simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
219 simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) && noexcept;
220 simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](std::string_view key) & noexcept;
221 simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](std::string_view key) && noexcept;
222 simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
223 inline simdjson_result<bool> reset() noexcept;
224 inline simdjson_result<bool> is_empty() noexcept;
225 inline simdjson_result<size_t> count_fields() & noexcept;
226
227};
228
229} // namespace simdjson
230