mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
Json pointer (#220)
* json pointer support * Addition of tests for the json pointer * Adding a new tool for the JSON Pointer support, and some documentation.
This commit is contained in:
@@ -93,4 +93,172 @@ bool ParsedJson::iterator::print(std::ostream &os, bool escape_strings) const {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParsedJson::iterator::move_to(const char * pointer, uint32_t length) {
|
||||
char* new_pointer = nullptr;
|
||||
if (pointer[0] == '#') {
|
||||
// Converting fragment representation to string representation
|
||||
new_pointer = new char[length];
|
||||
uint32_t new_length = 0;
|
||||
for (uint32_t i = 1; i < length; i++) {
|
||||
if (pointer[i] == '%' && pointer[i+1] == 'x') {
|
||||
try {
|
||||
int fragment = std::stoi(std::string(&pointer[i+2], 2), nullptr, 16);
|
||||
if (fragment == '\\' || fragment == '"' || (fragment <= 0x1F)) {
|
||||
// escaping the character
|
||||
new_pointer[new_length] = '\\';
|
||||
new_length++;
|
||||
}
|
||||
new_pointer[new_length] = fragment;
|
||||
i += 3;
|
||||
}
|
||||
catch(std::invalid_argument& e) {
|
||||
delete[] new_pointer;
|
||||
return false; // the fragment is invalid
|
||||
}
|
||||
}
|
||||
else {
|
||||
new_pointer[new_length] = pointer[i];
|
||||
}
|
||||
new_length++;
|
||||
}
|
||||
length = new_length;
|
||||
pointer = new_pointer;
|
||||
}
|
||||
|
||||
// saving the current state
|
||||
size_t depth_s = depth;
|
||||
size_t location_s = location;
|
||||
uint8_t current_type_s = current_type;
|
||||
uint64_t current_val_s = current_val;
|
||||
scopeindex_t *depthindex_s = depthindex;
|
||||
|
||||
rewind(); // The json pointer is used from the root of the document.
|
||||
|
||||
bool found = relative_move_to(pointer, length);
|
||||
delete[] new_pointer;
|
||||
|
||||
if (!found) {
|
||||
// since the pointer has found nothing, we get back to the original position.
|
||||
depth = depth_s;
|
||||
location = location_s;
|
||||
current_type = current_type_s;
|
||||
current_val = current_val_s;
|
||||
depthindex = depthindex_s;
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
bool ParsedJson::iterator::relative_move_to(const char * pointer, uint32_t length) {
|
||||
if (length == 0) {
|
||||
// returns the whole document
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pointer[0] != '/') {
|
||||
// '/' must be the first character
|
||||
return false;
|
||||
}
|
||||
|
||||
// finding the key in an object or the index in an array
|
||||
std::string key_or_index;
|
||||
uint32_t offset = 1;
|
||||
|
||||
// checking for the "-" case
|
||||
if (is_array() && pointer[1] == '-') {
|
||||
if (length != 2) {
|
||||
// the pointer must be exactly "/-"
|
||||
// there can't be anything more after '-' as an index
|
||||
return false;
|
||||
}
|
||||
key_or_index = '-';
|
||||
offset = length; // will skip the loop coming right after
|
||||
}
|
||||
|
||||
// We either transform the first reference token to a valid json key
|
||||
// or we make sure it is a valid index in an array.
|
||||
for (; offset < length ; offset++) {
|
||||
if (pointer[offset] == '/') {
|
||||
// beginning of the next key or index
|
||||
break;
|
||||
}
|
||||
if (is_array() && (pointer[offset] < '0' || pointer[offset] > '9')) {
|
||||
// the index of an array must be an integer
|
||||
// we also make sure std::stoi won't discard whitespaces later
|
||||
return false;
|
||||
}
|
||||
if (pointer[offset] == '~') {
|
||||
// "~1" represents "/"
|
||||
if (pointer[offset+1] == '1') {
|
||||
key_or_index += '/';
|
||||
offset++;
|
||||
continue;
|
||||
}
|
||||
// "~0" represents "~"
|
||||
if (pointer[offset+1] == '0') {
|
||||
key_or_index += '~';
|
||||
offset++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (pointer[offset] == '\\') {
|
||||
if (pointer[offset+1] == '\\' || pointer[offset+1] == '"' || (pointer[offset+1] <= 0x1F)) {
|
||||
key_or_index += pointer[offset+1];
|
||||
offset++;
|
||||
continue;
|
||||
}
|
||||
return false; // invalid escaped character
|
||||
}
|
||||
if (pointer[offset] == '\"') {
|
||||
// unescaped quote character. this is an invalid case.
|
||||
// lets do nothing and assume most pointers will be valid.
|
||||
// it won't find any corresponding json key anyway.
|
||||
// return false;
|
||||
}
|
||||
key_or_index += pointer[offset];
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
if (is_object()) {
|
||||
if (move_to_key(key_or_index.c_str(), key_or_index.length())) {
|
||||
found = relative_move_to(pointer+offset, length-offset);
|
||||
}
|
||||
}
|
||||
else if(is_array()) {
|
||||
if (key_or_index == "-") { // handling "-" case first
|
||||
if (down()) {
|
||||
while(next()); // moving to the end of the array
|
||||
// moving to the nonexistent value right after...
|
||||
size_t npos;
|
||||
if ((current_type == '[') || (current_type == '{')) {
|
||||
// we need to jump
|
||||
npos = ( current_val & JSONVALUEMASK);
|
||||
} else {
|
||||
npos = location + ((current_type == 'd' || current_type == 'l') ? 2 : 1);
|
||||
}
|
||||
location = npos;
|
||||
current_val = pj.tape[npos];
|
||||
current_type = (current_val >> 56);
|
||||
return true; // how could it fail ?
|
||||
}
|
||||
} else { // regular numeric index
|
||||
// The index can't have a leading '0'
|
||||
if (key_or_index[0] == '0' && key_or_index.length() > 1) {
|
||||
return false;
|
||||
}
|
||||
// it cannot be empty
|
||||
if (key_or_index.length() == 0) {
|
||||
return false;
|
||||
}
|
||||
// we already checked the index contains only valid digits
|
||||
uint32_t index = std::stoi(key_or_index);
|
||||
if (move_to_index(index)) {
|
||||
found = relative_move_to(pointer+offset, length-offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user