Skip to content

Commit 13aee51

Browse files
committed
Add element.type() for type switching
1 parent 7dad9fc commit 13aee51

File tree

5 files changed

+734
-64
lines changed

5 files changed

+734
-64
lines changed

doc/basics.md

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ An overview of what you need to know to use simdjson, with examples.
88
* [Using the Parsed JSON](#using-the-parsed-json)
99
* [JSON Pointer](#json-pointer)
1010
* [Error Handling](#error-handling)
11-
* [Error Handling Example](#error-handling-example)
12-
* [Exceptions](#exceptions)
11+
* [Error Handling Example](#error-handling-example)
12+
* [Exceptions](#exceptions)
13+
* [Tree Walking and JSON Element Types](#tree-walking-and-json-element-types)
1314
* [Newline-Delimited JSON (ndjson) and JSON lines](#newline-delimited-json-ndjson-and-json-lines)
1415
* [Thread Safety](#thread-safety)
1516

@@ -67,6 +68,8 @@ Once you have an element, you can navigate it with idiomatic C++ iterators, oper
6768
first element.
6869
> Note that array[0] does not compile, because implementing [] gives the impression indexing is a
6970
> O(1) operation, which it is not presently in simdjson.
71+
* **Checking an Element Type:** You can check an element's type with `element.type()`. It
72+
returns an `element_type`.
7073

7174
Here are some examples of all of the above:
7275

@@ -152,9 +155,9 @@ This is how the example in "Using the Parsed JSON" could be written using only e
152155
153156
```c++
154157
auto cars_json = R"( [
155-
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
156-
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
157-
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
158+
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
159+
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
160+
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
158161
] )"_padded;
159162
dom::parser parser;
160163
auto [cars, error] = parser.parse(cars_json).get<dom::array>();
@@ -210,6 +213,60 @@ dom::element doc = parser.parse(json); // Throws an exception if there was an er
210213
When used this way, a `simdjson_error` exception will be thrown if an error occurs, preventing the
211214
program from continuing if there was an error.
212215

216+
Tree Walking and JSON Element Types
217+
-----------------------------------
218+
219+
Sometimes you don't necessarily have a document with a known type, and are trying to generically
220+
inspect or walk over JSON elements. To do that, you can use iterators and the type() method. For
221+
example, here's a quick and dirty recursive function that verbosely prints the JSON document as JSON
222+
(* ignoring nuances like trailing commas and escaping strings, for brevity's sake):
223+
224+
```c++
225+
void print_json(dom::element element) {
226+
switch (element.type()) {
227+
case dom::element_type::ARRAY:
228+
cout << "[";
229+
for (dom::element child : dom::array(element)) {
230+
print_json(child);
231+
cout << ",";
232+
}
233+
cout << "]";
234+
break;
235+
case dom::element_type::OBJECT:
236+
cout << "{";
237+
for (dom::key_value_pair field : dom::object(element)) {
238+
cout << "\"" << field.key << "\": ";
239+
print_json(field.value);
240+
}
241+
cout << "}";
242+
break;
243+
case dom::element_type::INT64:
244+
cout << int64_t(element) << endl;
245+
break;
246+
case dom::element_type::UINT64:
247+
cout << uint64_t(element) << endl;
248+
break;
249+
case dom::element_type::DOUBLE:
250+
cout << double(element) << endl;
251+
break;
252+
case dom::element_type::STRING:
253+
cout << std::string_view(element) << endl;
254+
break;
255+
case dom::element_type::BOOL:
256+
cout << bool(element) << endl;
257+
break;
258+
case dom::element_type::NULL_VALUE:
259+
cout << "null" << endl;
260+
break;
261+
}
262+
}
263+
264+
void basics_treewalk_1() {
265+
dom::parser parser;
266+
print_json(parser.load("twitter.json"));
267+
}
268+
```
269+
213270
Newline-Delimited JSON (ndjson) and JSON lines
214271
----------------------------------------------
215272

include/simdjson/document.h

Lines changed: 88 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -41,50 +41,65 @@ namespace simdjson::internal {
4141
using namespace simdjson::dom;
4242

4343
constexpr const uint64_t JSON_VALUE_MASK = 0x00FFFFFFFFFFFFFF;
44-
enum class tape_type;
45-
class tape_ref;
46-
/**
47-
* The possible types in the tape. Internal only.
48-
*/
49-
enum class tape_type {
50-
ROOT = 'r',
51-
START_ARRAY = '[',
52-
START_OBJECT = '{',
53-
END_ARRAY = ']',
54-
END_OBJECT = '}',
55-
STRING = '"',
56-
INT64 = 'l',
57-
UINT64 = 'u',
58-
DOUBLE = 'd',
59-
TRUE_VALUE = 't',
60-
FALSE_VALUE = 'f',
61-
NULL_VALUE = 'n'
62-
};
6344

64-
/**
65-
* A reference to an element on the tape. Internal only.
66-
*/
67-
class tape_ref {
68-
public:
69-
really_inline tape_ref() noexcept;
70-
really_inline tape_ref(const document *doc, size_t json_index) noexcept;
71-
inline size_t after_element() const noexcept;
72-
really_inline tape_type type() const noexcept;
73-
really_inline uint64_t tape_value() const noexcept;
74-
template<typename T>
75-
really_inline T next_tape_value() const noexcept;
76-
inline std::string_view get_string_view() const noexcept;
77-
78-
/** The document this element references. */
79-
const document *doc;
80-
81-
/** The index of this element on `doc.tape[]` */
82-
size_t json_index;
83-
};
45+
/**
46+
* The possible types in the tape. Internal only.
47+
*/
48+
enum class tape_type {
49+
ROOT = 'r',
50+
START_ARRAY = '[',
51+
START_OBJECT = '{',
52+
END_ARRAY = ']',
53+
END_OBJECT = '}',
54+
STRING = '"',
55+
INT64 = 'l',
56+
UINT64 = 'u',
57+
DOUBLE = 'd',
58+
TRUE_VALUE = 't',
59+
FALSE_VALUE = 'f',
60+
NULL_VALUE = 'n'
61+
};
62+
63+
/**
64+
* A reference to an element on the tape. Internal only.
65+
*/
66+
class tape_ref {
67+
public:
68+
really_inline tape_ref() noexcept;
69+
really_inline tape_ref(const document *doc, size_t json_index) noexcept;
70+
inline size_t after_element() const noexcept;
71+
really_inline tape_type tape_ref_type() const noexcept;
72+
really_inline uint64_t tape_value() const noexcept;
73+
template<typename T>
74+
really_inline T next_tape_value() const noexcept;
75+
inline std::string_view get_string_view() const noexcept;
76+
77+
/** The document this element references. */
78+
const document *doc;
79+
80+
/** The index of this element on `doc.tape[]` */
81+
size_t json_index;
82+
};
83+
8484
} // namespace simdjson::internal
8585

8686
namespace simdjson::dom {
8787

88+
/**
89+
* The actual concrete type of a JSON element
90+
* This is the type it is most easily cast to with get<>.
91+
*/
92+
enum class element_type {
93+
ARRAY, ///< dom::array
94+
OBJECT, ///< dom::object
95+
INT64, ///< int64_t
96+
UINT64, ///< uint64_t: any integer that fits in uint64_t but *not* int64_t
97+
DOUBLE, ///< double: Any number with a "." or "e" that fits in double.
98+
STRING, ///< std::string_view
99+
BOOL, ///< bool
100+
NULL_VALUE ///< null
101+
};
102+
88103
/**
89104
* JSON array.
90105
*/
@@ -367,6 +382,9 @@ class element : protected internal::tape_ref {
367382
/** Create a new, invalid element. */
368383
really_inline element() noexcept;
369384

385+
/** The type of this element. */
386+
really_inline element_type type() const noexcept;
387+
370388
/** Whether this element is a json `null`. */
371389
really_inline bool is_null() const noexcept;
372390

@@ -1121,6 +1139,36 @@ inline std::ostream& operator<<(std::ostream& out, const object &value) { return
11211139
*/
11221140
inline std::ostream& operator<<(std::ostream& out, const key_value_pair &value) { return out << minify(value); }
11231141

1142+
/**
1143+
* Print element type to an output stream.
1144+
*
1145+
* @param out The output stream.
1146+
* @param value The value to print.
1147+
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
1148+
*/
1149+
inline std::ostream& operator<<(std::ostream& out, element_type type) {
1150+
switch (type) {
1151+
case element_type::ARRAY:
1152+
return out << "array";
1153+
case element_type::OBJECT:
1154+
return out << "object";
1155+
case element_type::INT64:
1156+
return out << "int64_t";
1157+
case element_type::UINT64:
1158+
return out << "uint64_t";
1159+
case element_type::DOUBLE:
1160+
return out << "double";
1161+
case element_type::STRING:
1162+
return out << "string";
1163+
case element_type::BOOL:
1164+
return out << "bool";
1165+
case element_type::NULL_VALUE:
1166+
return out << "null";
1167+
default:
1168+
abort();
1169+
}
1170+
}
1171+
11241172
} // namespace dom
11251173

11261174
#if SIMDJSON_EXCEPTIONS
@@ -1172,6 +1220,7 @@ struct simdjson_result<dom::element> : public internal::simdjson_result_base<dom
11721220
really_inline simdjson_result(dom::element &&value) noexcept; ///< @private
11731221
really_inline simdjson_result(error_code error) noexcept; ///< @private
11741222

1223+
inline simdjson_result<dom::element_type> type() const noexcept;
11751224
inline simdjson_result<bool> is_null() const noexcept;
11761225
template<typename T>
11771226
inline simdjson_result<bool> is() const noexcept;

0 commit comments

Comments
 (0)