| 1 | #include "simdjson/error.h" |
| 2 | |
| 3 | namespace simdjson { |
| 4 | namespace SIMDJSON_IMPLEMENTATION { |
| 5 | namespace ondemand { |
| 6 | |
| 7 | /** |
| 8 | * A forward-only JSON object field iterator. |
| 9 | */ |
| 10 | class object { |
| 11 | public: |
| 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 | |
| 181 | protected: |
| 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 | |
| 205 | namespace simdjson { |
| 206 | |
| 207 | template<> |
| 208 | struct simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object> : public SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::object> { |
| 209 | public: |
| 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 | |