Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: fix parse json path name with escaped characters #21

Merged
merged 2 commits into from
Jun 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 122 additions & 35 deletions src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@ use std::collections::VecDeque;
use crate::constants::*;
use crate::error::*;
use crate::jentry::JEntry;
use crate::jsonpath::ArrayIndex;
use crate::jsonpath::Index;
use crate::jsonpath::JsonPath;
use crate::jsonpath::Path;
use crate::jsonpath::Selector;
use crate::number::Number;
use crate::parser::parse_value;
Expand Down Expand Up @@ -184,27 +181,70 @@ pub fn get_by_path_array<'a>(value: &'a [u8], json_path: JsonPath<'a>) -> Option
}

/// Get the inner element of `JSONB` Array by index.
pub fn get_by_index(value: &[u8], index: i32) -> Option<Vec<u8>> {
if index < 0 {
return None;
pub fn get_by_index(value: &[u8], index: usize) -> Option<Vec<u8>> {
if !is_jsonb(value) {
return match parse_value(value) {
Ok(val) => match val {
Value::Array(vals) => vals.get(index).map(|v| v.to_vec()),
_ => None,
},
Err(_) => None,
};
}
let path = Path::ArrayIndices(vec![ArrayIndex::Index(Index::Index(index))]);
let json_path = JsonPath { paths: vec![path] };
get_by_path_first(value, json_path)
}

/// Get the inner element of `JSONB` Object by key name.
pub fn get_by_name(value: &[u8], name: &str) -> Option<Vec<u8>> {
let path = Path::DotField(Cow::Borrowed(name));
let json_path = JsonPath { paths: vec![path] };
get_by_path_first(value, json_path)
let header = read_u32(value, 0).unwrap();
match header & CONTAINER_HEADER_TYPE_MASK {
ARRAY_CONTAINER_TAG => {
let length = (header & CONTAINER_HEADER_LEN_MASK) as usize;
if index >= length {
return None;
}
let mut jentry_offset = 4;
let mut val_offset = 4 * length + 4;
for i in 0..length {
let encoded = read_u32(value, jentry_offset).unwrap();
let jentry = JEntry::decode_jentry(encoded);
let val_length = jentry.length as usize;
if i < index {
jentry_offset += 4;
val_offset += val_length;
continue;
}
let val = match jentry.type_code {
CONTAINER_TAG => value[val_offset..val_offset + val_length].to_vec(),
_ => {
let mut buf = Vec::with_capacity(8 + val_length);
buf.extend_from_slice(&SCALAR_CONTAINER_TAG.to_be_bytes());
buf.extend_from_slice(&encoded.to_be_bytes());
if jentry.length > 0 {
buf.extend_from_slice(&value[val_offset..val_offset + val_length]);
}
buf
}
};
return Some(val);
}
None
}
_ => None,
}
}

/// Get the inner element of `JSONB` Object by key name ignoring case.
pub fn get_by_name_ignore_case(value: &[u8], name: &str) -> Option<Vec<u8>> {
/// Get the inner element of `JSONB` Object by key name,
/// if `ignore_case` is true, enables case-insensitive matching.
pub fn get_by_name(value: &[u8], name: &str, ignore_case: bool) -> Option<Vec<u8>> {
if !is_jsonb(value) {
return match parse_value(value) {
Ok(val) => val.get_by_name_ignore_case(name).map(Value::to_vec),
Ok(val) => {
if ignore_case {
val.get_by_name_ignore_case(name).map(Value::to_vec)
} else {
match val {
Value::Object(obj) => obj.get(name).map(|v| v.to_vec()),
_ => None,
}
}
}
Err(_) => None,
};
}
Expand Down Expand Up @@ -238,32 +278,32 @@ pub fn get_by_name_ignore_case(value: &[u8], name: &str) -> Option<Vec<u8>> {
if name.eq(key) {
offsets = Some((jentry_offset, val_offset));
break;
} else if name.eq_ignore_ascii_case(key) && offsets.is_none() {
} else if ignore_case && name.eq_ignore_ascii_case(key) && offsets.is_none() {
offsets = Some((jentry_offset, val_offset));
}
let val_encoded = read_u32(value, jentry_offset).unwrap();
let val_jentry = JEntry::decode_jentry(val_encoded);
jentry_offset += 4;
val_offset += val_jentry.length as usize;
}
if let Some((jentry_offset, mut val_offset)) = offsets {
let mut buf: Vec<u8> = Vec::new();
if let Some((jentry_offset, val_offset)) = offsets {
let encoded = read_u32(value, jentry_offset).unwrap();
let jentry = JEntry::decode_jentry(encoded);
let prev_val_offset = val_offset;
val_offset += jentry.length as usize;
match jentry.type_code {
CONTAINER_TAG => buf.extend_from_slice(&value[prev_val_offset..val_offset]),
let val_length = jentry.length as usize;
let val = match jentry.type_code {
CONTAINER_TAG => value[val_offset..val_offset + val_length].to_vec(),
_ => {
let mut buf: Vec<u8> = Vec::with_capacity(val_length + 8);
let scalar_header = SCALAR_CONTAINER_TAG;
buf.extend_from_slice(&scalar_header.to_be_bytes());
buf.extend_from_slice(&encoded.to_be_bytes());
if val_offset > prev_val_offset {
buf.extend_from_slice(&value[prev_val_offset..val_offset]);
if val_length > 0 {
buf.extend_from_slice(&value[val_offset..val_offset + val_length]);
}
buf
}
}
return Some(buf);
};
return Some(val);
}
None
}
Expand Down Expand Up @@ -313,6 +353,54 @@ pub fn object_keys(value: &[u8]) -> Option<Vec<u8>> {
}
}

/// Convert the values of a `JSONB` array to vector.
pub fn array_values(value: &[u8]) -> Option<Vec<Vec<u8>>> {
if !is_jsonb(value) {
return match parse_value(value) {
Ok(val) => match val {
Value::Array(vals) => {
Some(vals.into_iter().map(|val| val.to_vec()).collect::<Vec<_>>())
}
_ => None,
},
Err(_) => None,
};
}

let header = read_u32(value, 0).unwrap();
match header & CONTAINER_HEADER_TYPE_MASK {
ARRAY_CONTAINER_TAG => {
let length = (header & CONTAINER_HEADER_LEN_MASK) as usize;
let mut jentry_offset = 4;
let mut val_offset = 4 * length + 4;
let mut items = Vec::with_capacity(length);
for _ in 0..length {
let encoded = read_u32(value, jentry_offset).unwrap();
let jentry = JEntry::decode_jentry(encoded);
let val_length = jentry.length as usize;
let item = match jentry.type_code {
CONTAINER_TAG => value[val_offset..val_offset + val_length].to_vec(),
_ => {
let mut buf = Vec::with_capacity(8 + val_length);
buf.extend_from_slice(&SCALAR_CONTAINER_TAG.to_be_bytes());
buf.extend_from_slice(&encoded.to_be_bytes());
if jentry.length > 0 {
buf.extend_from_slice(&value[val_offset..val_offset + val_length]);
}
buf
}
};
items.push(item);

jentry_offset += 4;
val_offset += val_length;
}
Some(items)
}
_ => None,
}
}

/// `JSONB` values supports partial decode for comparison,
/// if the values are found to be unequal, the result will be returned immediately.
/// In first level header, values compare as the following order:
Expand Down Expand Up @@ -863,8 +951,7 @@ pub fn is_object(value: &[u8]) -> bool {
/// Convert `JSONB` value to String
pub fn to_string(value: &[u8]) -> String {
if !is_jsonb(value) {
let json = unsafe { String::from_utf8_unchecked(value.to_vec()) };
return json;
return String::from_utf8_lossy(value).to_string();
}

let mut json = String::new();
Expand Down Expand Up @@ -971,15 +1058,15 @@ fn escape_scalar_string(value: &[u8], start: usize, end: usize, json: &mut Strin
}
};
if i > last_start {
let val = unsafe { std::str::from_utf8_unchecked(&value[last_start..i]) };
json.push_str(val);
let val = String::from_utf8_lossy(&value[last_start..i]);
json.push_str(&val);
}
json.push_str(c);
last_start = i + 1;
}
if last_start < end {
let val = unsafe { std::str::from_utf8_unchecked(&value[last_start..end]) };
json.push_str(val);
let val = String::from_utf8_lossy(&value[last_start..end]);
json.push_str(&val);
}
json.push('\"');
}
Expand Down
Loading