simdjson 5.0.2
Ridiculously Fast JSON
Loading...
Searching...
No Matches
object-inl.h
1#ifndef SIMDJSON_OBJECT_INL_H
2#define SIMDJSON_OBJECT_INL_H
3
4#include "simdjson/dom/base.h"
5#include "simdjson/dom/object.h"
6#include "simdjson/dom/document.h"
7
8#include "simdjson/dom/element-inl.h"
9#include "simdjson/error-inl.h"
10#include "simdjson/jsonpathutil.h"
11
12#include <cstring>
13
14namespace simdjson {
15
16//
17// simdjson_result<dom::object> inline implementation
18//
19simdjson_inline simdjson_result<dom::object>::simdjson_result() noexcept
20 : internal::simdjson_result_base<dom::object>() {}
21simdjson_inline simdjson_result<dom::object>::simdjson_result(dom::object value) noexcept
22 : internal::simdjson_result_base<dom::object>(std::forward<dom::object>(value)) {}
23simdjson_inline simdjson_result<dom::object>::simdjson_result(error_code error) noexcept
24 : internal::simdjson_result_base<dom::object>(error) {}
25
26inline simdjson_result<dom::element> simdjson_result<dom::object>::operator[](std::string_view key) const noexcept {
27 if (error()) { return error(); }
28 return first[key];
29}
30inline simdjson_result<dom::element> simdjson_result<dom::object>::operator[](const char *key) const noexcept {
31 if (error()) { return error(); }
32 return first[key];
33}
34inline simdjson_result<dom::element> simdjson_result<dom::object>::at_pointer(std::string_view json_pointer) const noexcept {
35 if (error()) { return error(); }
36 return first.at_pointer(json_pointer);
37}
38inline simdjson_result<dom::element> simdjson_result<dom::object>::at_path(std::string_view json_path) const noexcept {
39 if (error()) { return error(); }
40 auto json_pointer = json_path_to_pointer_conversion(json_path);
41 if (json_pointer == "-1") { return INVALID_JSON_POINTER; }
42 return at_pointer(json_pointer);
43}
44inline simdjson_result<std::vector<dom::element>> simdjson_result<dom::object>::at_path_with_wildcard(std::string_view json_path) const noexcept {
45 if (error()) {
46 return error();
47 }
48 return first.at_path_with_wildcard(json_path);
49}
50inline simdjson_result<dom::element> simdjson_result<dom::object>::at_key(std::string_view key) const noexcept {
51 if (error()) { return error(); }
52 return first.at_key(key);
53}
54inline std::vector<dom::element>& simdjson_result<dom::object>::get_values(std::vector<dom::element>& out) const noexcept {
55 return first.get_values(out);
56}
57inline simdjson_result<dom::element> simdjson_result<dom::object>::at_key_case_insensitive(std::string_view key) const noexcept {
58 if (error()) { return error(); }
59 return first.at_key_case_insensitive(key);
60}
61
62#if SIMDJSON_EXCEPTIONS
63
64inline dom::object::iterator simdjson_result<dom::object>::begin() const noexcept(false) {
65 if (error()) { throw simdjson_error(error()); }
66 return first.begin();
67}
68inline dom::object::iterator simdjson_result<dom::object>::end() const noexcept(false) {
69 if (error()) { throw simdjson_error(error()); }
70 return first.end();
71}
72inline size_t simdjson_result<dom::object>::size() const noexcept(false) {
73 if (error()) { throw simdjson_error(error()); }
74 return first.size();
75}
76
77#endif // SIMDJSON_EXCEPTIONS
78
79namespace dom {
80
81//
82// object inline implementation
83//
84simdjson_inline object::object() noexcept : tape{} {}
85simdjson_inline object::object(const internal::tape_ref &_tape) noexcept : tape{_tape} { }
86inline object::iterator object::begin() const noexcept {
87 SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
88 return internal::tape_ref(tape.doc, tape.json_index + 1);
89}
90inline object::iterator object::end() const noexcept {
91 SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
92 return internal::tape_ref(tape.doc, tape.after_element() - 1);
93}
94inline size_t object::size() const noexcept {
95 SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
96 return tape.scope_count();
97}
98
99inline simdjson_result<element> object::operator[](std::string_view key) const noexcept {
100 return at_key(key);
101}
102inline simdjson_result<element> object::operator[](const char *key) const noexcept {
103 return at_key(key);
104}
105inline simdjson_result<element> object::at_pointer(std::string_view json_pointer) const noexcept {
106 SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
107 if(json_pointer.empty()) { // an empty string means that we return the current node
108 return element(this->tape); // copy the current node
109 } else if(json_pointer[0] != '/') { // otherwise there is an error
111 }
112 json_pointer = json_pointer.substr(1);
113 size_t slash = json_pointer.find('/');
114 std::string_view key = json_pointer.substr(0, slash);
115 // Grab the child with the given key
117
118 // If there is an escape character in the key, unescape it and then get the child.
119 size_t escape = key.find('~');
120 if (escape != std::string_view::npos) {
121 // Unescape the key
122 std::string unescaped(key);
123 do {
124 switch (unescaped[escape+1]) {
125 case '0':
126 unescaped.replace(escape, 2, "~");
127 break;
128 case '1':
129 unescaped.replace(escape, 2, "/");
130 break;
131 default:
132 return INVALID_JSON_POINTER; // "Unexpected ~ escape character in JSON pointer");
133 }
134 escape = unescaped.find('~', escape+1);
135 } while (escape != std::string::npos);
136 child = at_key(unescaped);
137 } else {
138 child = at_key(key);
139 }
140 if(child.error()) {
141 return child; // we do not continue if there was an error
142 }
143 // If there is a /, we have to recurse and look up more of the path
144 if (slash != std::string_view::npos) {
145 child = child.at_pointer(json_pointer.substr(slash));
146 }
147 return child;
148}
149
150inline simdjson_result<element> object::at_path(std::string_view json_path) const noexcept {
151 auto json_pointer = json_path_to_pointer_conversion(json_path);
152 if (json_pointer == "-1") { return INVALID_JSON_POINTER; }
153 return at_pointer(json_pointer);
154}
155
156inline void object::process_json_path_of_child_elements(std::vector<element>::iterator& current, std::vector<element>::iterator& end, const std::string_view& path_suffix, std::vector<element>& accumulator) const noexcept {
157 if (current == end) {
158 return;
159 }
160
162
163 for (auto it = current; it != end; ++it) {
164 std::vector<element> child_result;
165 auto error = it->at_path_with_wildcard(path_suffix).get(child_result);
166 if(error) {
167 continue;
168 }
169 accumulator.insert(accumulator.end(),
170 std::make_move_iterator(child_result.begin()),
171 std::make_move_iterator(child_result.end()));
172 }
173}
174
175inline simdjson_result<std::vector<element>> object::at_path_with_wildcard(std::string_view json_path) const noexcept {
176 SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
177
178 size_t i = 0;
179 if (json_path.empty()) {
181 }
182 // if JSONPath starts with $, skip it
183 // json_path.starts_with('$') requires C++20.
184 if (json_path.front() == '$') {
185 i = 1;
186 }
187
188 if (i >= json_path.size() || (json_path[i] != '.' && json_path[i] != '[')) {
189 // expect JSONPath expressions to always start with $ but this isn't currently
190 // expected in jsonpathutil.h.
192 }
193
194 if (json_path.find("*") != std::string::npos) {
195
196 std::vector<element> child_values;
197
198 if (
199 (json_path.compare(i, 3, "[*]") == 0 && json_path.size() == i + 3) ||
200 (json_path.compare(i, 2,".*") == 0 && json_path.size() == i + 2)
201 ) {
202 get_values(child_values);
203 return child_values;
204 }
205
206 std::pair<std::string_view, std::string_view> key_and_json_path = get_next_key_and_json_path(json_path);
207
208 std::string_view key = key_and_json_path.first;
209 json_path = key_and_json_path.second;
210
211 if (key.size() > 0) {
212 if (key == "*") {
213 get_values(child_values);
214 } else {
215 element pointer_result;
216 auto error = at_pointer(std::string("/") + std::string(key)).get(pointer_result);
217
218 if (!error) {
219 child_values.emplace_back(pointer_result);
220 }
221 }
222
223 std::vector<element> result = {};
224 if (child_values.size() > 0) {
225
226 std::vector<element>::iterator child_values_begin = child_values.begin();
227 std::vector<element>::iterator child_values_end = child_values.end();
228
229 process_json_path_of_child_elements(child_values_begin, child_values_end, json_path, result);
230 }
231
232 return result;
233 } else {
235 }
236 } else {
237 element result;
238 auto error = this->at_path(json_path).get(result);
239 if (error) {
240 return error;
241 }
242 return std::vector<element>{std::move(result)};
243 }
244}
245
246inline simdjson_result<element> object::at_key(std::string_view key) const noexcept {
247 iterator end_field = end();
248 for (iterator field = begin(); field != end_field; ++field) {
249 if (field.key_equals(key)) {
250 return field.value();
251 }
252 }
253 return NO_SUCH_FIELD;
254}
255
256inline std::vector<element>& object::get_values(std::vector<element>& out) const noexcept {
257 iterator end_field = end();
258 iterator begin_field = begin();
259
260 out.reserve(std::distance(begin_field, end_field));
261 for (iterator field = begin_field; field != end_field; ++field) {
262 out.emplace_back(field.value());
263 }
264
265 return out;
266}
267// In case you wonder why we need this, please see
268// https://github.com/simdjson/simdjson/issues/323
269// People do seek keys in a case-insensitive manner.
270inline simdjson_result<element> object::at_key_case_insensitive(std::string_view key) const noexcept {
271 iterator end_field = end();
272 for (iterator field = begin(); field != end_field; ++field) {
273 if (field.key_equals_case_insensitive(key)) {
274 return field.value();
275 }
276 }
277 return NO_SUCH_FIELD;
278}
279
280inline object::operator element() const noexcept {
281 return element(tape);
282}
283
284//
285// object::iterator inline implementation
286//
287simdjson_inline object::iterator::iterator(const internal::tape_ref &_tape) noexcept : tape{_tape} { }
288inline const key_value_pair object::iterator::operator*() const noexcept {
289 return key_value_pair(key(), value());
290}
291inline bool object::iterator::operator!=(const object::iterator& other) const noexcept {
292 return tape.json_index != other.tape.json_index;
293}
294inline bool object::iterator::operator==(const object::iterator& other) const noexcept {
295 return tape.json_index == other.tape.json_index;
296}
297inline bool object::iterator::operator<(const object::iterator& other) const noexcept {
298 return tape.json_index < other.tape.json_index;
299}
300inline bool object::iterator::operator<=(const object::iterator& other) const noexcept {
301 return tape.json_index <= other.tape.json_index;
302}
303inline bool object::iterator::operator>=(const object::iterator& other) const noexcept {
304 return tape.json_index >= other.tape.json_index;
305}
306inline bool object::iterator::operator>(const object::iterator& other) const noexcept {
307 return tape.json_index > other.tape.json_index;
308}
310 tape.json_index++;
311 tape.json_index = tape.after_element();
312 return *this;
313}
315 object::iterator out = *this;
316 ++*this;
317 return out;
318}
319inline std::string_view object::iterator::key() const noexcept {
320 return tape.get_string_view();
321}
322inline uint32_t object::iterator::key_length() const noexcept {
323 return tape.get_string_length();
324}
325inline const char* object::iterator::key_c_str() const noexcept {
326 return reinterpret_cast<const char *>(&tape.doc->string_buf[size_t(tape.tape_value()) + sizeof(uint32_t)]);
327}
328inline element object::iterator::value() const noexcept {
329 return element(internal::tape_ref(tape.doc, tape.json_index + 1));
330}
331
345inline bool object::iterator::key_equals(std::string_view o) const noexcept {
346 // We use the fact that the key length can be computed quickly
347 // without access to the string buffer.
348 const uint32_t len = key_length();
349 if(o.size() == len) {
350 // We avoid construction of a temporary string_view instance.
351 return (memcmp(o.data(), key_c_str(), len) == 0);
352 }
353 return false;
354}
355
356inline bool object::iterator::key_equals_case_insensitive(std::string_view o) const noexcept {
357 // We use the fact that the key length can be computed quickly
358 // without access to the string buffer.
359 const uint32_t len = key_length();
360 if(o.size() == len) {
361 // See For case-insensitive string comparisons, avoid char-by-char functions
362 // https://lemire.me/blog/2020/04/30/for-case-insensitive-string-comparisons-avoid-char-by-char-functions/
363 // Note that it might be worth rolling our own strncasecmp function, with vectorization.
364 return (simdjson_strncasecmp(o.data(), key_c_str(), len) == 0);
365 }
366 return false;
367}
368//
369// key_value_pair inline implementation
370//
371inline key_value_pair::key_value_pair(std::string_view _key, element _value) noexcept :
372 key(_key), value(_value) {}
373
374} // namespace dom
375
376} // namespace simdjson
377
378#if SIMDJSON_SUPPORTS_RANGES
379static_assert(std::ranges::view<simdjson::dom::object>);
380static_assert(std::ranges::sized_range<simdjson::dom::object>);
381#if SIMDJSON_EXCEPTIONS
382static_assert(std::ranges::view<simdjson::simdjson_result<simdjson::dom::object>>);
383static_assert(std::ranges::sized_range<simdjson::simdjson_result<simdjson::dom::object>>);
384#endif // SIMDJSON_EXCEPTIONS
385#endif // SIMDJSON_SUPPORTS_RANGES
386
387#endif // SIMDJSON_OBJECT_INL_H
A JSON element.
Definition element.h:36
Key/value pair in an object.
Definition object.h:258
bool operator!=(const iterator &other) const noexcept
Check if these values come from the same place in the JSON.
Definition object-inl.h:291
element value() const noexcept
Get the value of this key/value pair.
Definition object-inl.h:328
bool key_equals(std::string_view o) const noexcept
Returns true if the key in this key/value pair is equal to the provided string_view.
Definition object-inl.h:345
const char * key_c_str() const noexcept
Get the key of this key/value pair.
Definition object-inl.h:325
uint32_t key_length() const noexcept
Get the length (in bytes) of the key in this key/value pair.
Definition object-inl.h:322
reference operator*() const noexcept
Get the actual key/value pair.
Definition object-inl.h:288
bool key_equals_case_insensitive(std::string_view o) const noexcept
Returns true if the key in this key/value pair is equal to the provided string_view in a case-insensi...
Definition object-inl.h:356
std::string_view key() const noexcept
Get the key of this key/value pair.
Definition object-inl.h:319
iterator & operator++() noexcept
Get the next key/value pair.
Definition object-inl.h:309
std::vector< element > & get_values(std::vector< element > &out) const noexcept
Gets the values associated with keys of an object This function has linear-time complexity: the keys ...
Definition object-inl.h:256
void process_json_path_of_child_elements(std::vector< element >::iterator &current, std::vector< element >::iterator &end, const std::string_view &path_suffix, std::vector< element > &accumulator) const noexcept
Recursive function which processes the JSON path of each child element.
Definition object-inl.h:156
simdjson_result< element > at_key(std::string_view key) const noexcept
Get the value associated with the given key.
Definition object-inl.h:246
iterator end() const noexcept
One past the last key/value pair.
Definition object-inl.h:90
simdjson_result< std::vector< element > > at_path_with_wildcard(std::string_view json_path) const noexcept
Adds support for JSONPath expression with wildcards '*'.
Definition object-inl.h:175
size_t size() const noexcept
Get the size of the object (number of keys).
Definition object-inl.h:94
simdjson_result< element > at_path(std::string_view json_path) const noexcept
Get the value associated with the given JSONPath expression.
Definition object-inl.h:150
simdjson_result< element > at_key_case_insensitive(std::string_view key) const noexcept
Get the value associated with the given key in a case-insensitive manner.
Definition object-inl.h:270
simdjson_result< element > operator[](std::string_view key) const noexcept
Get the value associated with the given key.
Definition object-inl.h:99
simdjson_result< element > at_pointer(std::string_view json_pointer) const noexcept
Get the value associated with the given JSON pointer.
Definition object-inl.h:105
simdjson_inline object() noexcept
Create a new, invalid object.
Definition object-inl.h:84
iterator begin() const noexcept
Return the first key/value pair.
Definition object-inl.h:86
The top level simdjson namespace, containing everything the library provides.
Definition base.h:8
error_code
All possible errors returned by simdjson.
Definition error.h:19
@ NO_SUCH_FIELD
JSON field not found in object.
Definition error.h:40
@ INVALID_JSON_POINTER
Invalid JSON pointer syntax.
Definition error.h:42
std::string json_path_to_pointer_conversion(std::string_view json_path)
Converts JSONPath to JSON Pointer.
The result of a simdjson operation that could fail.
Definition error.h:281
simdjson_inline error_code error() const noexcept
The error.
Definition error-inl.h:168