mirror of
https://github.com/fluencelabs/jsonpath
synced 2025-04-25 09:22:19 +00:00
support iterator
This commit is contained in:
parent
f919a5305b
commit
538b433ae2
16
src/lib.rs
16
src/lib.rs
@ -305,6 +305,18 @@ pub fn select<'a>(json: &'a Value, path: &str) -> Result<Vec<&'a Value>, JsonPat
|
||||
Selector::default().str_path(path)?.value(json).select()
|
||||
}
|
||||
|
||||
pub fn select_with_iter<'a>(
|
||||
json: impl ExactSizeIterator<Item = &'a Value> + 'a,
|
||||
path: &str,
|
||||
) -> Result<(Vec<&'a Value>, Vec<usize>), JsonPathError> {
|
||||
let mut selector = Selector::default();
|
||||
let json = selector.str_path(path)?.values_iter(json).select()?;
|
||||
|
||||
let chose_indices = selector.chose_indices();
|
||||
|
||||
Ok((json, chose_indices))
|
||||
}
|
||||
|
||||
/// It is the same to `select` function but it return the result as string.
|
||||
///
|
||||
/// ```rust
|
||||
@ -377,7 +389,9 @@ pub fn select_as<T: serde::de::DeserializeOwned>(
|
||||
path: &str,
|
||||
) -> Result<Vec<T>, JsonPathError> {
|
||||
let json = serde_json::from_str(json_str).map_err(|e| JsonPathError::Serde(e.to_string()))?;
|
||||
Selector::default().str_path(path)?.value(&json).select_as()
|
||||
let t = Selector::default().str_path(path)?.value(&json).select_as();
|
||||
|
||||
t
|
||||
}
|
||||
|
||||
/// Delete(= replace with null) the JSON property using the jsonpath.
|
||||
|
@ -13,8 +13,8 @@ mod utils {
|
||||
use std::str::FromStr;
|
||||
|
||||
pub fn string_to_num<F, S: FromStr>(string: &str, msg_handler: F) -> Result<S, String>
|
||||
where
|
||||
F: Fn() -> String,
|
||||
where
|
||||
F: Fn() -> String,
|
||||
{
|
||||
match string.parse() {
|
||||
Ok(n) => Ok(n),
|
||||
@ -543,21 +543,15 @@ impl Parser {
|
||||
_ => Self::paths(node, tokenizer),
|
||||
}
|
||||
}
|
||||
Ok(Token::Absolute(_)) => {
|
||||
Self::json_path(tokenizer)
|
||||
},
|
||||
Ok(Token::Absolute(_)) => Self::json_path(tokenizer),
|
||||
Ok(Token::DoubleQuoted(_, _)) | Ok(Token::SingleQuoted(_, _)) => {
|
||||
Self::array_quote_value(tokenizer)
|
||||
}
|
||||
Ok(Token::Key(_, key)) => match key.as_bytes()[0] {
|
||||
b'-' | b'0'..=b'9' => Self::term_num(tokenizer),
|
||||
_ => Self::boolean(tokenizer),
|
||||
},
|
||||
Ok(Token::Key(_, key)) => {
|
||||
match key.as_bytes()[0] {
|
||||
b'-' | b'0'..=b'9' => Self::term_num(tokenizer),
|
||||
_ => Self::boolean(tokenizer),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
Err(tokenizer.err_msg())
|
||||
}
|
||||
_ => Err(tokenizer.err_msg()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,95 +66,95 @@ impl Token {
|
||||
match self {
|
||||
Token::Absolute(_) => match other {
|
||||
Token::Absolute(_) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
},
|
||||
Token::Dot(_) => match other {
|
||||
Token::Dot(_) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
},
|
||||
Token::At(_) => match other {
|
||||
Token::At(_) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
},
|
||||
Token::OpenArray(_) => match other {
|
||||
Token::OpenArray(_) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
},
|
||||
Token::CloseArray(_) => match other {
|
||||
Token::CloseArray(_) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
},
|
||||
Token::Asterisk(_) => match other {
|
||||
Token::Asterisk(_) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
},
|
||||
Token::Question(_) => match other {
|
||||
Token::Question(_) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
},
|
||||
Token::Comma(_) => match other {
|
||||
Token::Comma(_) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
},
|
||||
Token::Split(_) => match other {
|
||||
Token::Split(_) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
},
|
||||
Token::OpenParenthesis(_) => match other {
|
||||
Token::OpenParenthesis(_) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
},
|
||||
Token::CloseParenthesis(_) => match other {
|
||||
Token::CloseParenthesis(_) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
},
|
||||
Token::Key(_, _) => match other {
|
||||
Token::Key(_, _) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
},
|
||||
Token::DoubleQuoted(_, _) => match other {
|
||||
Token::DoubleQuoted(_, _) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
},
|
||||
Token::SingleQuoted(_, _) => match other {
|
||||
Token::SingleQuoted(_, _) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
},
|
||||
Token::Equal(_) => match other {
|
||||
Token::Equal(_) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
},
|
||||
Token::GreaterOrEqual(_) => match other {
|
||||
Token::GreaterOrEqual(_) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
},
|
||||
Token::Greater(_) => match other {
|
||||
Token::Greater(_) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
},
|
||||
Token::Little(_) => match other {
|
||||
Token::Little(_) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
},
|
||||
Token::LittleOrEqual(_) => match other {
|
||||
Token::LittleOrEqual(_) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
},
|
||||
Token::NotEqual(_) => match other {
|
||||
Token::NotEqual(_) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
},
|
||||
Token::And(_) => match other {
|
||||
Token::And(_) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
},
|
||||
Token::Or(_) => match other {
|
||||
Token::Or(_) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
},
|
||||
Token::Whitespace(_, _) => match other {
|
||||
Token::Whitespace(_, _) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -174,23 +174,9 @@ impl<'a> Tokenizer<'a> {
|
||||
|
||||
fn dolla(&mut self, pos: usize, ch: char) -> Result<Token, TokenError> {
|
||||
let fun = |c: &char| match c {
|
||||
&CH_DOT
|
||||
| &CH_ASTERISK
|
||||
| &CH_LARRAY
|
||||
| &CH_RARRAY
|
||||
| &CH_LPAREN
|
||||
| &CH_RPAREN
|
||||
| &CH_AT
|
||||
| &CH_QUESTION
|
||||
| &CH_COMMA
|
||||
| &CH_SEMICOLON
|
||||
| &CH_LITTLE
|
||||
| &CH_GREATER
|
||||
| &CH_EQUAL
|
||||
| &CH_AMPERSAND
|
||||
| &CH_PIPE
|
||||
| &CH_EXCLAMATION
|
||||
=> false,
|
||||
&CH_DOT | &CH_ASTERISK | &CH_LARRAY | &CH_RARRAY | &CH_LPAREN | &CH_RPAREN | &CH_AT
|
||||
| &CH_QUESTION | &CH_COMMA | &CH_SEMICOLON | &CH_LITTLE | &CH_GREATER | &CH_EQUAL
|
||||
| &CH_AMPERSAND | &CH_PIPE | &CH_EXCLAMATION => false,
|
||||
_ => !c.is_whitespace(),
|
||||
};
|
||||
let (_, mut vec) = self.input.take_while(fun).map_err(to_token_error)?;
|
||||
@ -312,24 +298,9 @@ impl<'a> Tokenizer<'a> {
|
||||
|
||||
fn other(&mut self, pos: usize, ch: char) -> Result<Token, TokenError> {
|
||||
let fun = |c: &char| match c {
|
||||
&CH_DOLLA
|
||||
| &CH_DOT
|
||||
| &CH_ASTERISK
|
||||
| &CH_LARRAY
|
||||
| &CH_RARRAY
|
||||
| &CH_LPAREN
|
||||
| &CH_RPAREN
|
||||
| &CH_AT
|
||||
| &CH_QUESTION
|
||||
| &CH_COMMA
|
||||
| &CH_SEMICOLON
|
||||
| &CH_LITTLE
|
||||
| &CH_GREATER
|
||||
| &CH_EQUAL
|
||||
| &CH_AMPERSAND
|
||||
| &CH_PIPE
|
||||
| &CH_EXCLAMATION
|
||||
=> false,
|
||||
&CH_DOLLA | &CH_DOT | &CH_ASTERISK | &CH_LARRAY | &CH_RARRAY | &CH_LPAREN
|
||||
| &CH_RPAREN | &CH_AT | &CH_QUESTION | &CH_COMMA | &CH_SEMICOLON | &CH_LITTLE
|
||||
| &CH_GREATER | &CH_EQUAL | &CH_AMPERSAND | &CH_PIPE | &CH_EXCLAMATION => false,
|
||||
_ => !c.is_whitespace(),
|
||||
};
|
||||
let (_, mut vec) = self.input.take_while(fun).map_err(to_token_error)?;
|
||||
|
@ -175,7 +175,6 @@ impl Cmp for CmpOr {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod cmp_inner_tests {
|
||||
use serde_json::Value;
|
||||
|
@ -1,7 +1,8 @@
|
||||
use serde_json::{Number, Value};
|
||||
use select::cmp::*;
|
||||
use select::{FilterKey, to_f64};
|
||||
use select::{to_f64, FilterKey};
|
||||
use serde_json::{Number, Value};
|
||||
|
||||
// Parsed form of a json path.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(super) enum ExprTerm<'a> {
|
||||
String(String),
|
||||
@ -11,6 +12,7 @@ pub(super) enum ExprTerm<'a> {
|
||||
}
|
||||
|
||||
impl<'a> ExprTerm<'a> {
|
||||
// tries to match current json path with provided
|
||||
fn cmp<C1: Cmp, C2: Cmp>(
|
||||
&self,
|
||||
other: &Self,
|
||||
@ -201,11 +203,10 @@ impl<'a> Into<ExprTerm<'a>> for &Vec<&'a Value> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod expr_term_inner_tests {
|
||||
use serde_json::{Number, Value};
|
||||
use select::expr_term::ExprTerm;
|
||||
use serde_json::{Number, Value};
|
||||
|
||||
#[test]
|
||||
fn value_vec_into() {
|
||||
|
@ -1,13 +1,13 @@
|
||||
use std::collections::HashSet;
|
||||
use std::fmt;
|
||||
|
||||
use serde_json::{Number, Value};
|
||||
use serde_json::map::Entry;
|
||||
use serde_json::{Number, Value};
|
||||
|
||||
use parser::*;
|
||||
|
||||
use self::expr_term::*;
|
||||
use self::value_walker::ValueWalker;
|
||||
use self::value_walker::JValueWalker;
|
||||
|
||||
mod cmp;
|
||||
mod expr_term;
|
||||
@ -83,7 +83,9 @@ impl<'a> FilterTerms<'a> {
|
||||
self.0.pop()
|
||||
}
|
||||
|
||||
fn filter_json_term<F: Fn(&Vec<&'a Value>, &mut Vec<&'a Value>, &mut HashSet<usize>) -> FilterKey>(
|
||||
fn filter_json_term<
|
||||
F: Fn(&Vec<&'a Value>, &mut Vec<&'a Value>, &mut HashSet<usize>) -> FilterKey,
|
||||
>(
|
||||
&mut self,
|
||||
e: ExprTerm<'a>,
|
||||
fun: F,
|
||||
@ -94,36 +96,46 @@ impl<'a> FilterTerms<'a> {
|
||||
let mut tmp = Vec::new();
|
||||
let mut not_matched = HashSet::new();
|
||||
let filter_key = if let Some(FilterKey::String(key)) = fk {
|
||||
let key_contained = &vec.iter().map(|v| match v {
|
||||
Value::Object(map) if map.contains_key(&key) => map.get(&key).unwrap(),
|
||||
_ => v,
|
||||
}).collect();
|
||||
let key_contained = &vec
|
||||
.iter()
|
||||
.map(|v| match v {
|
||||
Value::Object(map) if map.contains_key(&key) => map.get(&key).unwrap(),
|
||||
_ => v,
|
||||
})
|
||||
.collect();
|
||||
fun(key_contained, &mut tmp, &mut not_matched)
|
||||
} else {
|
||||
fun(&vec, &mut tmp, &mut not_matched)
|
||||
};
|
||||
|
||||
if rel.is_some() {
|
||||
self.0.push(Some(ExprTerm::Json(rel, Some(filter_key), tmp)));
|
||||
self.0
|
||||
.push(Some(ExprTerm::Json(rel, Some(filter_key), tmp)));
|
||||
} else {
|
||||
let filtered: Vec<&Value> = vec.iter().enumerate()
|
||||
.filter(
|
||||
|(idx, _)| !not_matched.contains(idx)
|
||||
)
|
||||
let filtered: Vec<&Value> = vec
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(idx, _)| !not_matched.contains(idx))
|
||||
.map(|(_, v)| *v)
|
||||
.collect();
|
||||
|
||||
self.0.push(Some(ExprTerm::Json(Some(filtered), Some(filter_key), tmp)));
|
||||
self.0
|
||||
.push(Some(ExprTerm::Json(Some(filtered), Some(filter_key), tmp)));
|
||||
}
|
||||
} else {
|
||||
unreachable!("unexpected: ExprTerm: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn push_json_term<F: Fn(&Vec<&'a Value>, &mut Vec<&'a Value>, &mut HashSet<usize>) -> FilterKey>(
|
||||
fn push_json_term<
|
||||
F: Fn(&Vec<&'a Value>, &mut Vec<&'a Value>, &mut HashSet<usize>) -> FilterKey,
|
||||
FF: Fn(&Vec<&'a Value>, &mut Vec<&'a Value>, &mut HashSet<usize>) -> FilterKey,
|
||||
>(
|
||||
&mut self,
|
||||
current: &Option<Vec<&'a Value>>,
|
||||
values: Option<Box<dyn ExactSizeIterator<Item = &'a Value> + 'a>>,
|
||||
fun: F,
|
||||
fun_flattened: FF,
|
||||
) {
|
||||
debug!("push_json_term: {:?}", ¤t);
|
||||
|
||||
@ -131,68 +143,116 @@ impl<'a> FilterTerms<'a> {
|
||||
let mut tmp = Vec::new();
|
||||
let mut not_matched = HashSet::new();
|
||||
let filter_key = fun(current, &mut tmp, &mut not_matched);
|
||||
self.0.push(Some(ExprTerm::Json(None, Some(filter_key), tmp)));
|
||||
self.0
|
||||
.push(Some(ExprTerm::Json(None, Some(filter_key), tmp)));
|
||||
} else {
|
||||
if let Some(values) = values {
|
||||
let mut tmp = Vec::new();
|
||||
let mut not_matched = HashSet::new();
|
||||
let values = values.collect();
|
||||
let filter_key = fun_flattened(&values, &mut tmp, &mut not_matched);
|
||||
self.0
|
||||
.push(Some(ExprTerm::Json(None, Some(filter_key), tmp)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn filter<F: Fn(&Vec<&'a Value>, &mut Vec<&'a Value>, &mut HashSet<usize>) -> FilterKey>(
|
||||
fn filter<
|
||||
F: Fn(&Vec<&'a Value>, &mut Vec<&'a Value>, &mut HashSet<usize>) -> FilterKey,
|
||||
FF: Fn(&Vec<&'a Value>, &mut Vec<&'a Value>, &mut HashSet<usize>) -> FilterKey,
|
||||
>(
|
||||
&mut self,
|
||||
current: &Option<Vec<&'a Value>>,
|
||||
values: Option<Box<dyn ExactSizeIterator<Item = &'a Value> + 'a>>,
|
||||
fun: F,
|
||||
fun_flattened: FF,
|
||||
) {
|
||||
if let Some(peek) = self.0.pop() {
|
||||
if let Some(e) = peek {
|
||||
self.filter_json_term(e, fun);
|
||||
} else {
|
||||
self.push_json_term(current, fun);
|
||||
self.push_json_term(current, values, fun, fun_flattened);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn filter_all_with_str(&mut self, current: &Option<Vec<&'a Value>>, key: &str) {
|
||||
self.filter(current, |vec, tmp, _| {
|
||||
ValueWalker::all_with_str(&vec, tmp, key, true);
|
||||
FilterKey::All
|
||||
});
|
||||
fn filter_all_with_str(
|
||||
&mut self,
|
||||
current: &Option<Vec<&'a Value>>,
|
||||
values: Option<Box<dyn ExactSizeIterator<Item = &'a Value> + 'a>>,
|
||||
key: &str,
|
||||
) {
|
||||
self.filter(
|
||||
current,
|
||||
values,
|
||||
|vec, tmp, _| {
|
||||
JValueWalker::all_with_str(&vec, tmp, key, true);
|
||||
FilterKey::All
|
||||
},
|
||||
|_, _, _| FilterKey::All,
|
||||
);
|
||||
|
||||
debug!("filter_all_with_str : {}, {:?}", key, self.0);
|
||||
}
|
||||
|
||||
fn filter_next_with_str(&mut self, current: &Option<Vec<&'a Value>>, key: &str) {
|
||||
self.filter(current, |vec, tmp, not_matched| {
|
||||
let mut visited = HashSet::new();
|
||||
for (idx, v) in vec.iter().enumerate() {
|
||||
match v {
|
||||
Value::Object(map) => {
|
||||
if map.contains_key(key) {
|
||||
let ptr = *v as *const Value;
|
||||
if !visited.contains(&ptr) {
|
||||
visited.insert(ptr);
|
||||
tmp.push(v)
|
||||
fn filter_next_with_str(
|
||||
&mut self,
|
||||
current: &Option<Vec<&'a Value>>,
|
||||
values: Option<Box<dyn ExactSizeIterator<Item = &'a Value> + 'a>>,
|
||||
key: &str,
|
||||
) {
|
||||
self.filter(
|
||||
current,
|
||||
values,
|
||||
|vec, tmp, not_matched| {
|
||||
let mut visited = HashSet::new();
|
||||
for (idx, v) in vec.iter().enumerate() {
|
||||
match v {
|
||||
Value::Object(map) => {
|
||||
if map.contains_key(key) {
|
||||
let ptr = *v as *const Value;
|
||||
if !visited.contains(&ptr) {
|
||||
visited.insert(ptr);
|
||||
tmp.push(v)
|
||||
}
|
||||
} else {
|
||||
not_matched.insert(idx);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
Value::Array(vec) => {
|
||||
not_matched.insert(idx);
|
||||
for v in vec {
|
||||
JValueWalker::walk_dedup(v, tmp, key, &mut visited);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
not_matched.insert(idx);
|
||||
}
|
||||
}
|
||||
Value::Array(vec) => {
|
||||
not_matched.insert(idx);
|
||||
for v in vec {
|
||||
ValueWalker::walk_dedup(v, tmp, key, &mut visited);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
not_matched.insert(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FilterKey::String(key.to_owned())
|
||||
});
|
||||
FilterKey::String(key.to_owned())
|
||||
},
|
||||
|vec, tmp, not_matched| {
|
||||
let mut visited = HashSet::new();
|
||||
for (idx, v) in vec.iter().enumerate() {
|
||||
not_matched.insert(idx);
|
||||
JValueWalker::walk_dedup(v, tmp, key, &mut visited);
|
||||
}
|
||||
|
||||
FilterKey::String(key.to_owned())
|
||||
},
|
||||
);
|
||||
|
||||
debug!("filter_next_with_str : {}, {:?}", key, self.0);
|
||||
}
|
||||
|
||||
fn collect_next_with_num(&mut self, current: &Option<Vec<&'a Value>>, index: f64) -> Option<Vec<&'a Value>> {
|
||||
fn collect_next_with_num(
|
||||
&mut self,
|
||||
current: &Option<Vec<&'a Value>>,
|
||||
values: Option<Box<dyn ExactSizeIterator<Item = &'a Value> + 'a>>,
|
||||
index: f64,
|
||||
) -> (Option<Vec<&'a Value>>, Option<Vec<usize>>) {
|
||||
fn _collect<'a>(tmp: &mut Vec<&'a Value>, vec: &'a [Value], index: f64) {
|
||||
let index = abs_index(index as isize, vec.len());
|
||||
if let Some(v) = vec.get(index) {
|
||||
@ -220,21 +280,34 @@ impl<'a> FilterTerms<'a> {
|
||||
|
||||
if tmp.is_empty() {
|
||||
self.0.pop();
|
||||
return Some(vec![]);
|
||||
return (Some(vec![]), None);
|
||||
} else {
|
||||
return Some(tmp);
|
||||
return (Some(tmp), None);
|
||||
}
|
||||
}
|
||||
|
||||
debug!(
|
||||
"collect_next_with_num : {:?}, {:?}",
|
||||
&index, ¤t
|
||||
);
|
||||
if let Some(mut values) = values {
|
||||
let index = abs_index(index as isize, values.len());
|
||||
|
||||
None
|
||||
match values.nth(index) {
|
||||
Some(value) => return (Some(vec![value]), Some(vec![index])),
|
||||
None => {
|
||||
self.0.pop();
|
||||
return (Some(vec![]), None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug!("collect_next_with_num : {:?}, {:?}", &index, ¤t);
|
||||
|
||||
(None, None)
|
||||
}
|
||||
|
||||
fn collect_next_all(&mut self, current: &Option<Vec<&'a Value>>) -> Option<Vec<&'a Value>> {
|
||||
fn collect_next_all(
|
||||
&mut self,
|
||||
current: &Option<Vec<&'a Value>>,
|
||||
values: Option<Box<dyn ExactSizeIterator<Item = &'a Value> + 'a>>,
|
||||
) -> (Option<Vec<&'a Value>>, Option<Vec<usize>>) {
|
||||
if let Some(current) = current {
|
||||
let mut tmp = Vec::new();
|
||||
for c in current {
|
||||
@ -252,15 +325,27 @@ impl<'a> FilterTerms<'a> {
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
return Some(tmp);
|
||||
return (Some(tmp), None);
|
||||
}
|
||||
|
||||
if let Some(current) = values {
|
||||
let values = current.collect::<Vec<_>>();
|
||||
|
||||
let values_len = values.len();
|
||||
return (Some(values), Some((0..values_len).collect()));
|
||||
}
|
||||
|
||||
debug!("collect_next_all : {:?}", ¤t);
|
||||
|
||||
None
|
||||
(None, None)
|
||||
}
|
||||
|
||||
fn collect_next_with_str(&mut self, current: &Option<Vec<&'a Value>>, keys: &[String]) -> Option<Vec<&'a Value>> {
|
||||
fn collect_next_with_str(
|
||||
&mut self,
|
||||
current: &Option<Vec<&'a Value>>,
|
||||
values: Option<Box<dyn ExactSizeIterator<Item = &'a Value> + 'a>>,
|
||||
keys: &[String],
|
||||
) -> (Option<Vec<&'a Value>>, Option<Vec<usize>>) {
|
||||
if let Some(current) = current {
|
||||
let mut tmp = Vec::new();
|
||||
for c in current {
|
||||
@ -275,63 +360,99 @@ impl<'a> FilterTerms<'a> {
|
||||
|
||||
if tmp.is_empty() {
|
||||
self.0.pop();
|
||||
return Some(vec![]);
|
||||
return (Some(vec![]), None);
|
||||
} else {
|
||||
return Some(tmp);
|
||||
return (Some(tmp), None);
|
||||
}
|
||||
}
|
||||
|
||||
debug!(
|
||||
"collect_next_with_str : {:?}, {:?}",
|
||||
keys, ¤t
|
||||
);
|
||||
if values.is_some() {
|
||||
// values has array-like structure
|
||||
self.0.pop();
|
||||
return (Some(vec![]), None);
|
||||
}
|
||||
|
||||
None
|
||||
debug!("collect_next_with_str : {:?}, {:?}", keys, ¤t);
|
||||
|
||||
(None, None)
|
||||
}
|
||||
|
||||
fn collect_all(&mut self, current: &Option<Vec<&'a Value>>) -> Option<Vec<&'a Value>> {
|
||||
fn collect_all(
|
||||
&mut self,
|
||||
current: &Option<Vec<&'a Value>>,
|
||||
values: Option<Box<dyn ExactSizeIterator<Item = &'a Value> + 'a>>,
|
||||
) -> (Option<Vec<&'a Value>>, Option<Vec<usize>>) {
|
||||
if let Some(current) = current {
|
||||
let mut tmp = Vec::new();
|
||||
ValueWalker::all(¤t, &mut tmp);
|
||||
return Some(tmp);
|
||||
JValueWalker::all(¤t, &mut tmp);
|
||||
return (Some(tmp), None);
|
||||
}
|
||||
|
||||
if let Some(values) = values {
|
||||
let values = values.collect::<Vec<_>>();
|
||||
let values_len = values.len();
|
||||
return (Some(values), Some((0..values_len).collect()));
|
||||
}
|
||||
debug!("collect_all: {:?}", ¤t);
|
||||
|
||||
None
|
||||
(None, None)
|
||||
}
|
||||
|
||||
fn collect_all_with_str(&mut self, current: &Option<Vec<&'a Value>>, key: &str) -> Option<Vec<&'a Value>> {
|
||||
fn collect_all_with_str(
|
||||
&mut self,
|
||||
current: &Option<Vec<&'a Value>>,
|
||||
values: Option<Box<dyn ExactSizeIterator<Item = &'a Value> + 'a>>,
|
||||
key: &str,
|
||||
) -> (Option<Vec<&'a Value>>, Option<Vec<usize>>) {
|
||||
if let Some(current) = current {
|
||||
let mut tmp = Vec::new();
|
||||
ValueWalker::all_with_str(¤t, &mut tmp, key, false);
|
||||
return Some(tmp);
|
||||
JValueWalker::all_with_str(¤t, &mut tmp, key, false);
|
||||
return (Some(tmp), None);
|
||||
}
|
||||
|
||||
if values.is_some() {
|
||||
return (Some(vec![]), None);
|
||||
}
|
||||
|
||||
debug!("collect_all_with_str: {}, {:?}", key, ¤t);
|
||||
|
||||
None
|
||||
(None, None)
|
||||
}
|
||||
|
||||
fn collect_all_with_num(&mut self, current: &Option<Vec<&'a Value>>, index: f64) -> Option<Vec<&'a Value>> {
|
||||
fn collect_all_with_num(
|
||||
&mut self,
|
||||
current: &Option<Vec<&'a Value>>,
|
||||
values: Option<Box<dyn ExactSizeIterator<Item = &'a Value> + 'a>>,
|
||||
index: f64,
|
||||
) -> (Option<Vec<&'a Value>>, Option<Vec<usize>>) {
|
||||
if let Some(current) = current {
|
||||
let mut tmp = Vec::new();
|
||||
ValueWalker::all_with_num(¤t, &mut tmp, index);
|
||||
return Some(tmp);
|
||||
JValueWalker::all_with_num(¤t, &mut tmp, index);
|
||||
return (Some(tmp), None);
|
||||
}
|
||||
|
||||
if let Some(mut values) = values {
|
||||
match values.nth(index as usize) {
|
||||
Some(value) => return (Some(vec![value]), Some(vec![index as usize])),
|
||||
None => return (Some(vec![]), None),
|
||||
}
|
||||
}
|
||||
|
||||
debug!("collect_all_with_num: {}, {:?}", index, ¤t);
|
||||
|
||||
None
|
||||
(None, None)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Default)]
|
||||
pub struct Selector<'a, 'b> {
|
||||
node: Option<Node>,
|
||||
node_ref: Option<&'b Node>,
|
||||
value: Option<&'a Value>,
|
||||
tokens: Vec<ParseToken>,
|
||||
current: Option<Vec<&'a Value>>,
|
||||
values: Option<Box<dyn ExactSizeIterator<Item = &'a Value> + 'a>>,
|
||||
chose_indices: Vec<usize>,
|
||||
selectors: Vec<Selector<'a, 'b>>,
|
||||
selector_filter: FilterTerms<'a>,
|
||||
}
|
||||
@ -376,6 +497,14 @@ impl<'a, 'b> Selector<'a, 'b> {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn values_iter(
|
||||
&mut self,
|
||||
values: impl Iterator<Item = &'a Value> + ExactSizeIterator + 'a,
|
||||
) -> &mut Self {
|
||||
self.values = Some(Box::new(values));
|
||||
self
|
||||
}
|
||||
|
||||
fn _select(&mut self) -> Result<(), JsonPathError> {
|
||||
if self.node_ref.is_some() {
|
||||
let node_ref = self.node_ref.take().unwrap();
|
||||
@ -432,6 +561,15 @@ impl<'a, 'b> Selector<'a, 'b> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select_(&mut self) -> Result<Vec<&'a Value>, JsonPathError> {
|
||||
self._select()?;
|
||||
|
||||
match &self.current {
|
||||
Some(r) => Ok(r.to_vec()),
|
||||
_ => Err(JsonPathError::EmptyValue),
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_absolute_path_filter(&mut self, token: &ParseToken) -> bool {
|
||||
if !self.selectors.is_empty() {
|
||||
match token {
|
||||
@ -471,11 +609,17 @@ impl<'a, 'b> Selector<'a, 'b> {
|
||||
if let Some(value) = self.value {
|
||||
selector.value = Some(value);
|
||||
selector.current = Some(vec![value]);
|
||||
|
||||
println!("current.is_some: {:?}", selector.current);
|
||||
self.selectors.push(selector);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if self.values.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(v) = &self.value {
|
||||
self.current = Some(vec![v]);
|
||||
}
|
||||
@ -485,8 +629,13 @@ impl<'a, 'b> Selector<'a, 'b> {
|
||||
if let Some(ParseToken::Array) = self.tokens.last() {
|
||||
let array_token = self.tokens.pop();
|
||||
if let Some(ParseToken::Leaves) = self.tokens.last() {
|
||||
let values = std::mem::replace(&mut self.values, None);
|
||||
self.tokens.pop();
|
||||
self.current = self.selector_filter.collect_all(&self.current);
|
||||
let (current, chose_indices) =
|
||||
self.selector_filter.collect_all(&self.current, values);
|
||||
|
||||
self.current = current;
|
||||
self.chose_indices.extend(chose_indices.unwrap_or_default());
|
||||
}
|
||||
self.tokens.push(array_token.unwrap());
|
||||
}
|
||||
@ -496,8 +645,10 @@ impl<'a, 'b> Selector<'a, 'b> {
|
||||
fn visit_array_eof(&mut self) {
|
||||
if self.is_last_before_token_match(ParseToken::Array) {
|
||||
if let Some(Some(e)) = self.selector_filter.pop_term() {
|
||||
let values = std::mem::replace(&mut self.values, None);
|
||||
if let ExprTerm::String(key) = e {
|
||||
self.selector_filter.filter_next_with_str(&self.current, &key);
|
||||
self.selector_filter
|
||||
.filter_next_with_str(&self.current, values, &key);
|
||||
self.tokens.pop();
|
||||
return;
|
||||
}
|
||||
@ -510,22 +661,36 @@ impl<'a, 'b> Selector<'a, 'b> {
|
||||
self.tokens.pop();
|
||||
self.tokens.pop();
|
||||
if let Some(Some(e)) = self.selector_filter.pop_term() {
|
||||
let selector_filter_consumed = match &e {
|
||||
ExprTerm::Number(n) => {
|
||||
self.current = self.selector_filter.collect_all_with_num(&self.current, to_f64(n));
|
||||
self.selector_filter.pop_term();
|
||||
true
|
||||
}
|
||||
ExprTerm::String(key) => {
|
||||
self.current = self.selector_filter.collect_all_with_str(&self.current, key);
|
||||
self.selector_filter.pop_term();
|
||||
true
|
||||
}
|
||||
_ => {
|
||||
self.selector_filter.push_term(Some(e));
|
||||
false
|
||||
}
|
||||
};
|
||||
let selector_filter_consumed =
|
||||
match &e {
|
||||
ExprTerm::Number(n) => {
|
||||
let values = std::mem::replace(&mut self.values, None);
|
||||
let (current, chose_indices) = self
|
||||
.selector_filter
|
||||
.collect_all_with_num(&self.current, values, to_f64(n));
|
||||
|
||||
self.selector_filter.pop_term();
|
||||
|
||||
self.current = current;
|
||||
self.chose_indices.extend(chose_indices.unwrap_or_default());
|
||||
true
|
||||
}
|
||||
ExprTerm::String(key) => {
|
||||
let values = std::mem::replace(&mut self.values, None);
|
||||
let (current, chose_indices) = self
|
||||
.selector_filter
|
||||
.collect_all_with_str(&self.current, values, key);
|
||||
|
||||
self.selector_filter.pop_term();
|
||||
self.current = current;
|
||||
self.chose_indices.extend(chose_indices.unwrap_or_default());
|
||||
true
|
||||
}
|
||||
_ => {
|
||||
self.selector_filter.push_term(Some(e));
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
if selector_filter_consumed {
|
||||
return;
|
||||
@ -536,10 +701,25 @@ impl<'a, 'b> Selector<'a, 'b> {
|
||||
if let Some(Some(e)) = self.selector_filter.pop_term() {
|
||||
match e {
|
||||
ExprTerm::Number(n) => {
|
||||
self.current = self.selector_filter.collect_next_with_num(&self.current, to_f64(&n));
|
||||
let values = std::mem::replace(&mut self.values, None);
|
||||
let (current, chose_indices) = self.selector_filter.collect_next_with_num(
|
||||
&self.current,
|
||||
values,
|
||||
to_f64(&n),
|
||||
);
|
||||
|
||||
self.current = current;
|
||||
self.chose_indices.extend(chose_indices.unwrap_or_default());
|
||||
}
|
||||
ExprTerm::String(key) => {
|
||||
self.current = self.selector_filter.collect_next_with_str(&self.current, &[key]);
|
||||
let values = std::mem::replace(&mut self.values, None);
|
||||
|
||||
let (current, chose_indices) =
|
||||
self.selector_filter
|
||||
.collect_next_with_str(&self.current, values, &[key]);
|
||||
|
||||
self.current = current;
|
||||
self.chose_indices.extend(chose_indices.unwrap_or_default());
|
||||
}
|
||||
ExprTerm::Json(rel, _, v) => {
|
||||
if v.is_empty() {
|
||||
@ -573,24 +753,38 @@ impl<'a, 'b> Selector<'a, 'b> {
|
||||
self.tokens.pop();
|
||||
}
|
||||
|
||||
let values = std::mem::replace(&mut self.values, None);
|
||||
match self.tokens.last() {
|
||||
Some(ParseToken::Leaves) => {
|
||||
self.tokens.pop();
|
||||
self.current = self.selector_filter.collect_all(&self.current);
|
||||
let (current, chose_indices) =
|
||||
self.selector_filter.collect_all(&self.current, values);
|
||||
|
||||
self.current = current;
|
||||
self.chose_indices.extend(chose_indices.unwrap_or_default());
|
||||
}
|
||||
Some(ParseToken::In) => {
|
||||
self.tokens.pop();
|
||||
self.current = self.selector_filter.collect_next_all(&self.current);
|
||||
let (current, chose_indices) =
|
||||
self.selector_filter.collect_next_all(&self.current, values);
|
||||
|
||||
self.current = current;
|
||||
self.chose_indices.extend(chose_indices.unwrap_or_default());
|
||||
}
|
||||
_ => {
|
||||
self.current = self.selector_filter.collect_next_all(&self.current);
|
||||
let (current, chose_indices) =
|
||||
self.selector_filter.collect_next_all(&self.current, values);
|
||||
|
||||
self.current = current;
|
||||
self.chose_indices.extend(chose_indices.unwrap_or_default());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_key(&mut self, key: &str) {
|
||||
if let Some(ParseToken::Array) = self.tokens.last() {
|
||||
self.selector_filter.push_term(Some(ExprTerm::String(key.to_string())));
|
||||
self.selector_filter
|
||||
.push_term(Some(ExprTerm::String(key.to_string())));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -598,20 +792,38 @@ impl<'a, 'b> Selector<'a, 'b> {
|
||||
if self.selector_filter.is_term_empty() {
|
||||
match t {
|
||||
ParseToken::Leaves => {
|
||||
self.current = self.selector_filter.collect_all_with_str(&self.current, key)
|
||||
let values = std::mem::replace(&mut self.values, None);
|
||||
let (current, chose_indices) =
|
||||
self.selector_filter
|
||||
.collect_all_with_str(&self.current, values, key);
|
||||
|
||||
self.current = current;
|
||||
self.chose_indices.extend(chose_indices.unwrap_or_default());
|
||||
}
|
||||
ParseToken::In => {
|
||||
self.current = self.selector_filter.collect_next_with_str(&self.current, &[key.to_string()])
|
||||
let values = std::mem::replace(&mut self.values, None);
|
||||
let (current, chose_indices) = self.selector_filter.collect_next_with_str(
|
||||
&self.current,
|
||||
values,
|
||||
&[key.to_string()],
|
||||
);
|
||||
|
||||
self.current = current;
|
||||
self.chose_indices.extend(chose_indices.unwrap_or_default());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else {
|
||||
match t {
|
||||
ParseToken::Leaves => {
|
||||
self.selector_filter.filter_all_with_str(&self.current, key);
|
||||
let values = std::mem::replace(&mut self.values, None);
|
||||
self.selector_filter
|
||||
.filter_all_with_str(&self.current, values, key);
|
||||
}
|
||||
ParseToken::In => {
|
||||
self.selector_filter.filter_next_with_str(&self.current, key);
|
||||
let values = std::mem::replace(&mut self.values, None);
|
||||
self.selector_filter
|
||||
.filter_next_with_str(&self.current, values, key);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@ -625,7 +837,12 @@ impl<'a, 'b> Selector<'a, 'b> {
|
||||
}
|
||||
|
||||
if let Some(ParseToken::Array) = self.tokens.pop() {
|
||||
self.current = self.selector_filter.collect_next_with_str(&self.current, keys);
|
||||
let values = std::mem::replace(&mut self.values, None);
|
||||
let (current, chose_indices) =
|
||||
self.selector_filter
|
||||
.collect_next_with_str(&self.current, values, keys);
|
||||
self.current = current;
|
||||
self.chose_indices.extend(chose_indices.unwrap_or_default());
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
@ -685,28 +902,54 @@ impl<'a, 'b> Selector<'a, 'b> {
|
||||
if let Some(current) = &self.current {
|
||||
for v in current {
|
||||
if let Value::Array(vec) = v {
|
||||
let from = if let Some(from) = from {
|
||||
abs_index(*from, vec.len())
|
||||
} else {
|
||||
0
|
||||
let from = match from {
|
||||
Some(from) => abs_index(*from, vec.len()),
|
||||
None => 0,
|
||||
};
|
||||
|
||||
let to = if let Some(to) = to {
|
||||
abs_index(*to, vec.len())
|
||||
} else {
|
||||
vec.len()
|
||||
let to = match to {
|
||||
Some(to) => abs_index(*to, vec.len()),
|
||||
None => vec.len(),
|
||||
};
|
||||
|
||||
for i in (from..to).step_by(match step {
|
||||
let step = match step {
|
||||
Some(step) => *step,
|
||||
_ => 1,
|
||||
}) {
|
||||
None => 1,
|
||||
};
|
||||
|
||||
for i in (from..to).step_by(step) {
|
||||
if let Some(v) = vec.get(i) {
|
||||
tmp.push(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let values = std::mem::replace(&mut self.values, None);
|
||||
if let Some(values) = values {
|
||||
let from = match from {
|
||||
Some(from) => abs_index(*from, values.len()),
|
||||
None => 0,
|
||||
};
|
||||
|
||||
let to = match to {
|
||||
Some(to) => abs_index(*to, values.len()),
|
||||
None => values.len(),
|
||||
};
|
||||
|
||||
let step = match step {
|
||||
Some(step) => *step,
|
||||
None => 1,
|
||||
};
|
||||
|
||||
let take_count = if from > to { 0 } else { to - from };
|
||||
|
||||
let matched_values = values.skip(from).step_by(step).take(take_count);
|
||||
tmp.extend(matched_values);
|
||||
|
||||
self.chose_indices
|
||||
.extend((from..to).step_by(step).collect::<Vec<_>>())
|
||||
}
|
||||
}
|
||||
self.current = Some(tmp);
|
||||
} else {
|
||||
@ -731,6 +974,21 @@ impl<'a, 'b> Selector<'a, 'b> {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let indices = indices.iter().map(|&v| v as usize).collect::<HashSet<_>>();
|
||||
let values = std::mem::replace(&mut self.values, None);
|
||||
if let Some(values) = values {
|
||||
let new_values = values
|
||||
.enumerate()
|
||||
.filter(|(id, _)| indices.contains(id))
|
||||
.map(|(id, value)| {
|
||||
self.chose_indices.push(id);
|
||||
value
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
tmp.extend(new_values);
|
||||
}
|
||||
}
|
||||
|
||||
self.current = Some(tmp);
|
||||
@ -738,6 +996,10 @@ impl<'a, 'b> Selector<'a, 'b> {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chose_indices(self) -> Vec<usize> {
|
||||
self.chose_indices
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> NodeVisitor for Selector<'a, 'b> {
|
||||
@ -762,7 +1024,8 @@ impl<'a, 'b> NodeVisitor for Selector<'a, 'b> {
|
||||
ParseToken::Key(key) => self.visit_key(key),
|
||||
ParseToken::Keys(keys) => self.visit_keys(keys),
|
||||
ParseToken::Number(v) => {
|
||||
self.selector_filter.push_term(Some(ExprTerm::Number(Number::from_f64(*v).unwrap())));
|
||||
self.selector_filter
|
||||
.push_term(Some(ExprTerm::Number(Number::from_f64(*v).unwrap())));
|
||||
}
|
||||
ParseToken::Filter(ref ft) => self.visit_filter(ft),
|
||||
ParseToken::Range(from, to, step) => self.visit_range(from, to, step),
|
||||
@ -961,7 +1224,6 @@ impl SelectorMut {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod select_inner_tests {
|
||||
use serde_json::Value;
|
||||
@ -998,4 +1260,4 @@ mod select_inner_tests {
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,20 @@
|
||||
use serde_json::Value;
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub(super) struct ValueWalker;
|
||||
pub(super) struct JValueWalker;
|
||||
|
||||
impl<'a> ValueWalker {
|
||||
impl<'a> JValueWalker {
|
||||
pub fn all_with_num(vec: &[&'a Value], tmp: &mut Vec<&'a Value>, index: f64) {
|
||||
Self::walk(vec, tmp, &|v| if v.is_array() {
|
||||
if let Some(item) = v.get(index as usize) {
|
||||
Some(vec![item])
|
||||
Self::walk(vec, tmp, &|v| {
|
||||
if v.is_array() {
|
||||
if let Some(item) = v.get(index as usize) {
|
||||
Some(vec![item])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
});
|
||||
}
|
||||
|
||||
@ -47,13 +49,19 @@ impl<'a> ValueWalker {
|
||||
});
|
||||
}
|
||||
|
||||
fn walk<F>(vec: &[&'a Value], tmp: &mut Vec<&'a Value>, fun: &F) where F: Fn(&Value) -> Option<Vec<&Value>> {
|
||||
fn walk<F>(vec: &[&'a Value], tmp: &mut Vec<&'a Value>, fun: &F)
|
||||
where
|
||||
F: Fn(&Value) -> Option<Vec<&Value>>,
|
||||
{
|
||||
for v in vec {
|
||||
Self::_walk(v, tmp, fun);
|
||||
}
|
||||
}
|
||||
|
||||
fn _walk<F>(v: &'a Value, tmp: &mut Vec<&'a Value>, fun: &F) where F: Fn(&Value) -> Option<Vec<&Value>> {
|
||||
fn _walk<F>(v: &'a Value, tmp: &mut Vec<&'a Value>, fun: &F)
|
||||
where
|
||||
F: Fn(&Value) -> Option<Vec<&Value>>,
|
||||
{
|
||||
if let Some(mut ret) = fun(v) {
|
||||
tmp.append(&mut ret);
|
||||
}
|
||||
@ -73,10 +81,12 @@ impl<'a> ValueWalker {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn walk_dedup(v: &'a Value,
|
||||
tmp: &mut Vec<&'a Value>,
|
||||
key: &str,
|
||||
visited: &mut HashSet<*const Value>, ) {
|
||||
pub fn walk_dedup(
|
||||
v: &'a Value,
|
||||
tmp: &mut Vec<&'a Value>,
|
||||
key: &str,
|
||||
visited: &mut HashSet<*const Value>,
|
||||
) {
|
||||
match v {
|
||||
Value::Object(map) => {
|
||||
if map.contains_key(key) {
|
||||
@ -96,4 +106,3 @@ impl<'a> ValueWalker {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,4 +250,4 @@ fn bugs40_bracket_notation_after_recursive_descent() {
|
||||
"more"
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -133,11 +133,11 @@ fn filter_parent_paths() {
|
||||
select_and_then_compare(
|
||||
"$[?(@.key.subKey == 'subKey2')]",
|
||||
json!([
|
||||
{"key": {"seq": 1, "subKey": "subKey1"}},
|
||||
{"key": {"seq": 2, "subKey": "subKey2"}},
|
||||
{"key": 42},
|
||||
{"some": "value"}
|
||||
]),
|
||||
{"key": {"seq": 1, "subKey": "subKey1"}},
|
||||
{"key": {"seq": 2, "subKey": "subKey2"}},
|
||||
{"key": 42},
|
||||
{"some": "value"}
|
||||
]),
|
||||
json!([{"key": {"seq": 2, "subKey": "subKey2"}}]),
|
||||
);
|
||||
}
|
||||
@ -149,15 +149,15 @@ fn bugs33_exist_in_all() {
|
||||
select_and_then_compare(
|
||||
"$..[?(@.first.second)]",
|
||||
json!({
|
||||
"foo": {
|
||||
"first": { "second": "value" }
|
||||
},
|
||||
"foo2": {
|
||||
"first": {}
|
||||
},
|
||||
"foo3": {
|
||||
}
|
||||
}),
|
||||
"foo": {
|
||||
"first": { "second": "value" }
|
||||
},
|
||||
"foo2": {
|
||||
"first": {}
|
||||
},
|
||||
"foo3": {
|
||||
}
|
||||
}),
|
||||
json!([
|
||||
{
|
||||
"first": {
|
||||
@ -175,15 +175,15 @@ fn bugs33_exist_left_in_all_with_and_condition() {
|
||||
select_and_then_compare(
|
||||
"$..[?(@.first && @.first.second)]",
|
||||
json!({
|
||||
"foo": {
|
||||
"first": { "second": "value" }
|
||||
},
|
||||
"foo2": {
|
||||
"first": {}
|
||||
},
|
||||
"foo3": {
|
||||
}
|
||||
}),
|
||||
"foo": {
|
||||
"first": { "second": "value" }
|
||||
},
|
||||
"foo2": {
|
||||
"first": {}
|
||||
},
|
||||
"foo3": {
|
||||
}
|
||||
}),
|
||||
json!([
|
||||
{
|
||||
"first": {
|
||||
@ -232,49 +232,49 @@ fn bugs38_array_notation_in_filter() {
|
||||
select_and_then_compare(
|
||||
"$[?(@['key']==42)]",
|
||||
json!([
|
||||
{"key": 0},
|
||||
{"key": 42},
|
||||
{"key": -1},
|
||||
{"key": 41},
|
||||
{"key": 43},
|
||||
{"key": 42.0001},
|
||||
{"key": 41.9999},
|
||||
{"key": 100},
|
||||
{"some": "value"}
|
||||
]),
|
||||
{"key": 0},
|
||||
{"key": 42},
|
||||
{"key": -1},
|
||||
{"key": 41},
|
||||
{"key": 43},
|
||||
{"key": 42.0001},
|
||||
{"key": 41.9999},
|
||||
{"key": 100},
|
||||
{"some": "value"}
|
||||
]),
|
||||
json!([{"key": 42}]),
|
||||
);
|
||||
|
||||
select_and_then_compare(
|
||||
"$[?(@['key'].subKey == 'subKey2')]",
|
||||
json!([
|
||||
{"key": {"seq": 1, "subKey": "subKey1"}},
|
||||
{"key": {"seq": 2, "subKey": "subKey2"}},
|
||||
{"key": 42},
|
||||
{"some": "value"}
|
||||
]),
|
||||
{"key": {"seq": 1, "subKey": "subKey1"}},
|
||||
{"key": {"seq": 2, "subKey": "subKey2"}},
|
||||
{"key": 42},
|
||||
{"some": "value"}
|
||||
]),
|
||||
json!([{"key": {"seq": 2, "subKey": "subKey2"}}]),
|
||||
);
|
||||
|
||||
select_and_then_compare(
|
||||
"$[?(@['key']['subKey'] == 'subKey2')]",
|
||||
json!([
|
||||
{"key": {"seq": 1, "subKey": "subKey1"}},
|
||||
{"key": {"seq": 2, "subKey": "subKey2"}},
|
||||
{"key": 42},
|
||||
{"some": "value"}
|
||||
]),
|
||||
{"key": {"seq": 1, "subKey": "subKey1"}},
|
||||
{"key": {"seq": 2, "subKey": "subKey2"}},
|
||||
{"key": 42},
|
||||
{"some": "value"}
|
||||
]),
|
||||
json!([{"key": {"seq": 2, "subKey": "subKey2"}}]),
|
||||
);
|
||||
|
||||
select_and_then_compare(
|
||||
"$..key[?(@['subKey'] == 'subKey2')]",
|
||||
json!([
|
||||
{"key": {"seq": 1, "subKey": "subKey1"}},
|
||||
{"key": {"seq": 2, "subKey": "subKey2"}},
|
||||
{"key": 42},
|
||||
{"some": "value"}
|
||||
]),
|
||||
{"key": {"seq": 1, "subKey": "subKey1"}},
|
||||
{"key": {"seq": 2, "subKey": "subKey2"}},
|
||||
{"key": 42},
|
||||
{"some": "value"}
|
||||
]),
|
||||
json!([{"seq": 2, "subKey": "subKey2"}]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
49
tests/op.rs
49
tests/op.rs
@ -174,14 +174,14 @@ fn op_ge_for_number() {
|
||||
select_and_then_compare("$.[?(@.a >= 0)]", json!({ "a": 1 }), json!([{ "a": 1 }]));
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[test]
|
||||
fn op_eq_for_string_value() {
|
||||
setup();
|
||||
|
||||
select_and_then_compare(
|
||||
r#"$.[?(@.a == "b")]"#, json!({ "a": "b" }), json!([{ "a": "b" }]),
|
||||
r#"$.[?(@.a == "b")]"#,
|
||||
json!({ "a": "b" }),
|
||||
json!([{ "a": "b" }]),
|
||||
);
|
||||
}
|
||||
|
||||
@ -190,18 +190,17 @@ fn op_ne_for_string_value() {
|
||||
setup();
|
||||
|
||||
select_and_then_compare(
|
||||
r#"$.[?(@.a != "c")]"#, json!({ "a": "b" }), json!([{ "a": "b" }]),
|
||||
r#"$.[?(@.a != "c")]"#,
|
||||
json!({ "a": "b" }),
|
||||
json!([{ "a": "b" }]),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn op_lt_for_string_value() {
|
||||
setup();
|
||||
|
||||
select_and_then_compare(
|
||||
r#"$.[?(@.a < "b")]"#, json!({ "a": "b" }), json!([]),
|
||||
);
|
||||
select_and_then_compare(r#"$.[?(@.a < "b")]"#, json!({ "a": "b" }), json!([]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -209,7 +208,9 @@ fn op_le_for_string_value() {
|
||||
setup();
|
||||
|
||||
select_and_then_compare(
|
||||
r#"$.[?(@.a <= "b")]"#, json!({ "a": "b" }), json!([{ "a": "b" }]),
|
||||
r#"$.[?(@.a <= "b")]"#,
|
||||
json!({ "a": "b" }),
|
||||
json!([{ "a": "b" }]),
|
||||
);
|
||||
}
|
||||
|
||||
@ -217,9 +218,7 @@ fn op_le_for_string_value() {
|
||||
fn op_gt_for_string_value() {
|
||||
setup();
|
||||
|
||||
select_and_then_compare(
|
||||
r#"$.[?(@.a > "b")]"#, json!({ "a": "b" }), json!([]),
|
||||
);
|
||||
select_and_then_compare(r#"$.[?(@.a > "b")]"#, json!({ "a": "b" }), json!([]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -227,7 +226,9 @@ fn op_ge_for_string_value() {
|
||||
setup();
|
||||
|
||||
select_and_then_compare(
|
||||
r#"$.[?(@.a >= "b")]"#, json!({ "a": "b" }), json!([{ "a": "b" }]),
|
||||
r#"$.[?(@.a >= "b")]"#,
|
||||
json!({ "a": "b" }),
|
||||
json!([{ "a": "b" }]),
|
||||
);
|
||||
}
|
||||
|
||||
@ -301,11 +302,7 @@ fn op_ge_for_object_value() {
|
||||
fn op_eq_for_complex_value() {
|
||||
setup();
|
||||
|
||||
select_and_then_compare(
|
||||
r#"$.[?(1 == @.a)]"#,
|
||||
json!({ "a": { "b": 1 } }),
|
||||
json!([]),
|
||||
);
|
||||
select_and_then_compare(r#"$.[?(1 == @.a)]"#, json!({ "a": { "b": 1 } }), json!([]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -323,22 +320,14 @@ fn op_ne_for_complex_value() {
|
||||
fn op_le_for_complex_value() {
|
||||
setup();
|
||||
|
||||
select_and_then_compare(
|
||||
r#"$.[?(@.a <= 1)]"#,
|
||||
json!({ "a": { "b": 1 } }),
|
||||
json!([]),
|
||||
);
|
||||
select_and_then_compare(r#"$.[?(@.a <= 1)]"#, json!({ "a": { "b": 1 } }), json!([]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn op_gt_for_complex_value() {
|
||||
setup();
|
||||
|
||||
select_and_then_compare(
|
||||
r#"$.[?(@.a > "1")]"#,
|
||||
json!({ "a": { "b": 1 } }),
|
||||
json!([]),
|
||||
);
|
||||
select_and_then_compare(r#"$.[?(@.a > "1")]"#, json!({ "a": { "b": 1 } }), json!([]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -351,7 +340,7 @@ fn op_compare_different_types() {
|
||||
r#"$[?(true == 1)]"#,
|
||||
r#"$[?(@ == 1)]"#,
|
||||
]
|
||||
.iter()
|
||||
.iter()
|
||||
{
|
||||
select_and_then_compare(path, json!({}), json!([]));
|
||||
}
|
||||
@ -373,4 +362,4 @@ fn op_for_same_type() {
|
||||
{"a": 1}
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
178
tests/paths.rs
178
tests/paths.rs
@ -12,104 +12,104 @@ fn dolla_token_in_path() {
|
||||
select_and_then_compare(
|
||||
"$..$ref",
|
||||
json!({
|
||||
"Junk1": "This is a test to illustrate use of '$' in the attr for the expression $..['$ref'] ",
|
||||
"$ref": "Match Root",
|
||||
"Subset1":[
|
||||
{"Junk2": "Data...",
|
||||
"$ref": "Match Subset1"
|
||||
}
|
||||
],
|
||||
"hierachy1":{
|
||||
"hierachy2.1":{
|
||||
"hierachy2.1.1":{ "$ref":"Match 2.1.1"},
|
||||
"hierachy2.1.2":{ "ref":"Match 2.1.2"},
|
||||
"hierachy2.1.3":{ "ref":"No Match 2.1.3"},
|
||||
"hierachy2.1.4":{ "$ref":"Match 2.1.4"},
|
||||
"hierachy2.1.5":{ "ref":"No Match 2.1.5"}
|
||||
},
|
||||
"hierachy2.2":{
|
||||
"hierachy2.2.1":{ "ref":"No Match 2.2.1"},
|
||||
"hierachy2.2.2":{ "$ref":"Match 2.2.2"},
|
||||
"hierachy2.2.3":{ "ref":"No Match 2.2.3"},
|
||||
"hierachy2.2.4":{ "ref":"No Match 2.2.5"},
|
||||
"hierachy2.2.5":{ "$ref":"Match 2.2.5"}
|
||||
},
|
||||
"hierachy2.3":{
|
||||
"hierachy2.3.1":{ "ref":"No Match 2.3.1"},
|
||||
"hierachy2.3.2":{ "ref":"No Match 2.3.2"},
|
||||
"hierachy2.3.3":{ "ref":"No Match 2.3.3"},
|
||||
"hierachy2.3.4":{ "ref":"No Match 2.3.4"},
|
||||
"hierachy2.3.5":{ "ref":"No Match 2.3.5"},
|
||||
"hierachy2.3.6":{
|
||||
"hierachy2.3.6.1":{ "$ref":"Match 2.3.6.1"},
|
||||
"hierachy2.3.6.2":{ "ref":"No Match 2.3.6.2"},
|
||||
"hierachy2.3.6.3":{ "ref":"No Match 2.3.6.3"},
|
||||
"hierachy2.3.6.4":{ "ref":"No Match 2.3.6.4"},
|
||||
"hierachy2.3.6.5":{ "ref":"No Match 2.3.6.5"}
|
||||
}
|
||||
}
|
||||
"Junk1": "This is a test to illustrate use of '$' in the attr for the expression $..['$ref'] ",
|
||||
"$ref": "Match Root",
|
||||
"Subset1":[
|
||||
{"Junk2": "Data...",
|
||||
"$ref": "Match Subset1"
|
||||
}
|
||||
}),
|
||||
],
|
||||
"hierachy1":{
|
||||
"hierachy2.1":{
|
||||
"hierachy2.1.1":{ "$ref":"Match 2.1.1"},
|
||||
"hierachy2.1.2":{ "ref":"Match 2.1.2"},
|
||||
"hierachy2.1.3":{ "ref":"No Match 2.1.3"},
|
||||
"hierachy2.1.4":{ "$ref":"Match 2.1.4"},
|
||||
"hierachy2.1.5":{ "ref":"No Match 2.1.5"}
|
||||
},
|
||||
"hierachy2.2":{
|
||||
"hierachy2.2.1":{ "ref":"No Match 2.2.1"},
|
||||
"hierachy2.2.2":{ "$ref":"Match 2.2.2"},
|
||||
"hierachy2.2.3":{ "ref":"No Match 2.2.3"},
|
||||
"hierachy2.2.4":{ "ref":"No Match 2.2.5"},
|
||||
"hierachy2.2.5":{ "$ref":"Match 2.2.5"}
|
||||
},
|
||||
"hierachy2.3":{
|
||||
"hierachy2.3.1":{ "ref":"No Match 2.3.1"},
|
||||
"hierachy2.3.2":{ "ref":"No Match 2.3.2"},
|
||||
"hierachy2.3.3":{ "ref":"No Match 2.3.3"},
|
||||
"hierachy2.3.4":{ "ref":"No Match 2.3.4"},
|
||||
"hierachy2.3.5":{ "ref":"No Match 2.3.5"},
|
||||
"hierachy2.3.6":{
|
||||
"hierachy2.3.6.1":{ "$ref":"Match 2.3.6.1"},
|
||||
"hierachy2.3.6.2":{ "ref":"No Match 2.3.6.2"},
|
||||
"hierachy2.3.6.3":{ "ref":"No Match 2.3.6.3"},
|
||||
"hierachy2.3.6.4":{ "ref":"No Match 2.3.6.4"},
|
||||
"hierachy2.3.6.5":{ "ref":"No Match 2.3.6.5"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
json!([
|
||||
"Match Root",
|
||||
"Match Subset1",
|
||||
"Match 2.1.1",
|
||||
"Match 2.1.4",
|
||||
"Match 2.2.2",
|
||||
"Match 2.2.5",
|
||||
"Match 2.3.6.1"
|
||||
"Match Root",
|
||||
"Match Subset1",
|
||||
"Match 2.1.1",
|
||||
"Match 2.1.4",
|
||||
"Match 2.2.2",
|
||||
"Match 2.2.5",
|
||||
"Match 2.3.6.1"
|
||||
]),
|
||||
);
|
||||
|
||||
select_and_then_compare(
|
||||
"$..['$ref']",
|
||||
json!({
|
||||
"Junk1": "This is a test to illustrate use of '$' in the attr for the expression $..['$ref'] ",
|
||||
"$ref": "Match Root",
|
||||
"Subset1":[
|
||||
{"Junk2": "Data...",
|
||||
"$ref": "Match Subset1"
|
||||
}
|
||||
],
|
||||
"hierachy1":{
|
||||
"hierachy2.1":{
|
||||
"hierachy2.1.1":{ "$ref":"Match 2.1.1"},
|
||||
"hierachy2.1.2":{ "ref":"Match 2.1.2"},
|
||||
"hierachy2.1.3":{ "ref":"No Match 2.1.3"},
|
||||
"hierachy2.1.4":{ "$ref":"Match 2.1.4"},
|
||||
"hierachy2.1.5":{ "ref":"No Match 2.1.5"}
|
||||
},
|
||||
"hierachy2.2":{
|
||||
"hierachy2.2.1":{ "ref":"No Match 2.2.1"},
|
||||
"hierachy2.2.2":{ "$ref":"Match 2.2.2"},
|
||||
"hierachy2.2.3":{ "ref":"No Match 2.2.3"},
|
||||
"hierachy2.2.4":{ "ref":"No Match 2.2.5"},
|
||||
"hierachy2.2.5":{ "$ref":"Match 2.2.5"}
|
||||
},
|
||||
"hierachy2.3":{
|
||||
"hierachy2.3.1":{ "ref":"No Match 2.3.1"},
|
||||
"hierachy2.3.2":{ "ref":"No Match 2.3.2"},
|
||||
"hierachy2.3.3":{ "ref":"No Match 2.3.3"},
|
||||
"hierachy2.3.4":{ "ref":"No Match 2.3.4"},
|
||||
"hierachy2.3.5":{ "ref":"No Match 2.3.5"},
|
||||
"hierachy2.3.6":{
|
||||
"hierachy2.3.6.1":{ "$ref":"Match 2.3.6.1"},
|
||||
"hierachy2.3.6.2":{ "ref":"No Match 2.3.6.2"},
|
||||
"hierachy2.3.6.3":{ "ref":"No Match 2.3.6.3"},
|
||||
"hierachy2.3.6.4":{ "ref":"No Match 2.3.6.4"},
|
||||
"hierachy2.3.6.5":{ "ref":"No Match 2.3.6.5"}
|
||||
}
|
||||
}
|
||||
"Junk1": "This is a test to illustrate use of '$' in the attr for the expression $..['$ref'] ",
|
||||
"$ref": "Match Root",
|
||||
"Subset1":[
|
||||
{"Junk2": "Data...",
|
||||
"$ref": "Match Subset1"
|
||||
}
|
||||
}),
|
||||
],
|
||||
"hierachy1":{
|
||||
"hierachy2.1":{
|
||||
"hierachy2.1.1":{ "$ref":"Match 2.1.1"},
|
||||
"hierachy2.1.2":{ "ref":"Match 2.1.2"},
|
||||
"hierachy2.1.3":{ "ref":"No Match 2.1.3"},
|
||||
"hierachy2.1.4":{ "$ref":"Match 2.1.4"},
|
||||
"hierachy2.1.5":{ "ref":"No Match 2.1.5"}
|
||||
},
|
||||
"hierachy2.2":{
|
||||
"hierachy2.2.1":{ "ref":"No Match 2.2.1"},
|
||||
"hierachy2.2.2":{ "$ref":"Match 2.2.2"},
|
||||
"hierachy2.2.3":{ "ref":"No Match 2.2.3"},
|
||||
"hierachy2.2.4":{ "ref":"No Match 2.2.5"},
|
||||
"hierachy2.2.5":{ "$ref":"Match 2.2.5"}
|
||||
},
|
||||
"hierachy2.3":{
|
||||
"hierachy2.3.1":{ "ref":"No Match 2.3.1"},
|
||||
"hierachy2.3.2":{ "ref":"No Match 2.3.2"},
|
||||
"hierachy2.3.3":{ "ref":"No Match 2.3.3"},
|
||||
"hierachy2.3.4":{ "ref":"No Match 2.3.4"},
|
||||
"hierachy2.3.5":{ "ref":"No Match 2.3.5"},
|
||||
"hierachy2.3.6":{
|
||||
"hierachy2.3.6.1":{ "$ref":"Match 2.3.6.1"},
|
||||
"hierachy2.3.6.2":{ "ref":"No Match 2.3.6.2"},
|
||||
"hierachy2.3.6.3":{ "ref":"No Match 2.3.6.3"},
|
||||
"hierachy2.3.6.4":{ "ref":"No Match 2.3.6.4"},
|
||||
"hierachy2.3.6.5":{ "ref":"No Match 2.3.6.5"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
json!([
|
||||
"Match Root",
|
||||
"Match Subset1",
|
||||
"Match 2.1.1",
|
||||
"Match 2.1.4",
|
||||
"Match 2.2.2",
|
||||
"Match 2.2.5",
|
||||
"Match 2.3.6.1"
|
||||
"Match Root",
|
||||
"Match Subset1",
|
||||
"Match 2.1.1",
|
||||
"Match 2.1.4",
|
||||
"Match 2.2.2",
|
||||
"Match 2.2.5",
|
||||
"Match 2.3.6.1"
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -100,9 +100,5 @@ fn return_type_for_array_filter_true() {
|
||||
fn return_type_empty() {
|
||||
setup();
|
||||
|
||||
select_and_then_compare(
|
||||
"$[?(@.key==43)]",
|
||||
json!([{"key": 42}]),
|
||||
json!([]),
|
||||
);
|
||||
}
|
||||
select_and_then_compare("$[?(@.key==43)]", json!([{"key": 42}]), json!([]));
|
||||
}
|
||||
|
@ -120,12 +120,58 @@ fn selector_remove() {
|
||||
.select()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
vec![
|
||||
&json!(8.95),
|
||||
&json!(12.99),
|
||||
&json!(8.99)
|
||||
]
|
||||
);
|
||||
assert_eq!(result, vec![&json!(8.95), &json!(12.99), &json!(8.99)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iter_test() {
|
||||
use std::rc::Rc;
|
||||
|
||||
struct T {
|
||||
pub value: Rc<Value>,
|
||||
pub tt: i32,
|
||||
};
|
||||
|
||||
let t = Value::Array(vec![
|
||||
Value::String(String::from("vv")),
|
||||
Value::Array(vec![]),
|
||||
]);
|
||||
let t1 = T {
|
||||
value: Rc::new(t.clone()),
|
||||
tt: 1,
|
||||
};
|
||||
let t2 = T {
|
||||
value: Rc::new(t.clone()),
|
||||
tt: 1,
|
||||
};
|
||||
|
||||
let values = vec![t1, t2];
|
||||
|
||||
let mut selector = Selector::default();
|
||||
|
||||
let path = "$.[0]";
|
||||
let result = selector
|
||||
.str_path(path)
|
||||
.unwrap()
|
||||
.values_iter(values.iter().map(|v| v.value.as_ref()))
|
||||
.select()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(result, vec![&json!(["vv", []])]);
|
||||
|
||||
let result = jsonpath::select_with_iter(values.iter().map(|v| v.value.as_ref()), path).unwrap();
|
||||
assert_eq!(result.0, vec![&json!(["vv", []])]);
|
||||
assert_eq!(result.1, vec![0]);
|
||||
|
||||
let values = vec![json!([1, 2, 3, 4]), json!([2, 2]), json!(3)];
|
||||
|
||||
let mut selector = Selector::default();
|
||||
let result = selector
|
||||
.str_path(path)
|
||||
.unwrap()
|
||||
.values_iter(values.iter())
|
||||
.select()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(result, vec![&json!([1, 2, 3, 4])]);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user