#ifndef SIMDJSON_JSONPATHUTIL_H #define SIMDJSON_JSONPATHUTIL_H #include "simdjson/error.h" #include #include "simdjson/common_defs.h" #include #include namespace simdjson { namespace internal { /** * Parses the next JSON Pointer array index token. * * The caller passes a pointer fragment with no leading '/', such as "123/foo". * On success, array_index receives the parsed index and token_length receives * the number of bytes consumed before the next '/' or the end of the fragment. */ simdjson_inline error_code parse_json_pointer_array_index(std::string_view json_pointer, size_t &array_index, size_t &token_length) noexcept { array_index = 0; token_length = 0; for (; token_length < json_pointer.length() && json_pointer[token_length] != '/'; token_length++) { uint8_t digit = uint8_t(json_pointer[token_length] - '0'); // Check for non-digit in array index. If it's there, we're trying to get a field in an object. if (digit > 9) { return INCORRECT_TYPE; } // 0 followed by other digits is invalid. if (token_length > 0 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } if (array_index > (((std::numeric_limits::max)() - digit) / 10)) { return INDEX_OUT_OF_BOUNDS; } array_index = array_index * 10 + digit; } // Empty string is invalid; so is a "/" with no digits before it. if (token_length == 0) { return INVALID_JSON_POINTER; } return SUCCESS; } } // namespace internal /** * Converts JSONPath to JSON Pointer. * @param json_path The JSONPath string to be converted. * @return A string containing the equivalent JSON Pointer. */ inline std::string json_path_to_pointer_conversion(std::string_view json_path) { size_t i = 0; // if JSONPath starts with $, skip it // json_path.starts_with('$') requires C++20. if (!json_path.empty() && json_path.front() == '$') { i = 1; } if (i >= json_path.size() || (json_path[i] != '.' && json_path[i] != '[')) { return "-1"; // This is just a sentinel value, the caller should check for this and return an error. } std::string result; // Reserve space to reduce allocations, adjusting for potential increases due // to escaping. result.reserve(json_path.size() * 2); while (i < json_path.length()) { if (json_path[i] == '.') { result += '/'; } else if (json_path[i] == '[') { result += '/'; ++i; // Move past the '[' while (i < json_path.length() && json_path[i] != ']') { if (json_path[i] == '~') { result += "~0"; } else if (json_path[i] == '/') { result += "~1"; } else { result += json_path[i]; } ++i; } if (i == json_path.length() || json_path[i] != ']') { return "-1"; // Using sentinel value that will be handled as an error by the caller. } } else { if (json_path[i] == '~') { result += "~0"; } else if (json_path[i] == '/') { result += "~1"; } else { result += json_path[i]; } } ++i; } return result; } inline std::pair get_next_key_and_json_path(std::string_view& json_path) { std::string_view key; if (json_path.empty()) { return {key, json_path}; } size_t i = 0; // if JSONPath starts with $, skip it if (json_path.front() == '$') { i = 1; } if (i < json_path.length() && json_path[i] == '.') { i += 1; size_t key_start = i; while (i < json_path.length() && json_path[i] != '[' && json_path[i] != '.') { ++i; } key = json_path.substr(key_start, i - key_start); } else if ((i+1 < json_path.size()) && json_path[i] == '[' && (json_path[i+1] == '\'' || json_path[i+1] == '"')) { i += 2; size_t key_start = i; while (i < json_path.length() && json_path[i] != '\'' && json_path[i] != '"') { ++i; } key = json_path.substr(key_start, i - key_start); i += 2; } else if ((i+2 < json_path.size()) && json_path[i] == '[' && json_path[i+1] == '*' && json_path[i+2] == ']') { // i.e [*].additional_keys or [*]["additional_keys"] key = "*"; i += 3; } return std::make_pair(key, json_path.substr(i)); } } // namespace simdjson #endif // SIMDJSON_JSONPATHUTIL_H