mirror of
https://github.com/fluencelabs/jsonpath
synced 2025-04-25 09:22:19 +00:00
Json to Json comparator 수정
This commit is contained in:
parent
abc541d303
commit
cdf0446a69
@ -120,10 +120,8 @@ json 데이터 *(참고 사이트: https://github.com/json-path/JsonPath)*
|
|||||||
| <a href="https://freestrings.github.io/jsonpath/?path=$..book[2:]" target="_blank">$..book[2:]</a> | Book number two from tail |
|
| <a href="https://freestrings.github.io/jsonpath/?path=$..book[2:]" target="_blank">$..book[2:]</a> | Book number two from tail |
|
||||||
| <a href="https://freestrings.github.io/jsonpath/?path=$..book[?(@.isbn)]" target="_blank">$..book[?(@.isbn)]</a> | All books with an ISBN number |
|
| <a href="https://freestrings.github.io/jsonpath/?path=$..book[?(@.isbn)]" target="_blank">$..book[?(@.isbn)]</a> | All books with an ISBN number |
|
||||||
| <a href="https://freestrings.github.io/jsonpath/?path=$.store.book[?(@.price < 10)]" target="_blank">$.store.book[?(@.price < 10)]</a> | All books in store cheaper than 10 |
|
| <a href="https://freestrings.github.io/jsonpath/?path=$.store.book[?(@.price < 10)]" target="_blank">$.store.book[?(@.price < 10)]</a> | All books in store cheaper than 10 |
|
||||||
| $..book[?(@.price <= $['expensive'])] *(not yet supported)* | ~~All books in store that are not "expensive"~~ |
|
| <a href="https://freestrings.github.io/jsonpath/?path=$..*" target="_blank">$..*</a> | Give me every thing
|
||||||
| $..book[?(@.author =~ /.*REES/i)] *(not yet supported)* | ~~All books matching regex (ignore case)~~ |
|
| <a href="https://freestrings.github.io/jsonpath/?path=%24..book%5B%3F((%40.price%20%3D%3D%2012.99%20%7C%7C%20%24.store.bicycle.price%20%3C%20%40.price)%20%7C%7C%20%40.category%20%3D%3D%20%22reference%22)%5D" target="_blank">$..book[?((@.price == 12.99 | | $.store.bicycle.price < @.price) || @.category == "reference")]</a> | Complex filter
|
||||||
| <a href="https://freestrings.github.io/jsonpath/?path=$..*" target="_blank">$..*</a> | Give me every thing
|
|
||||||
| $..book.length() *(not yet supported)* | ~~The number of books~~ |
|
|
||||||
|
|
||||||
|
|
||||||
## With Rust (as library)
|
## With Rust (as library)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#[derive(Debug)]
|
||||||
pub enum CmpType {
|
pub enum CmpType {
|
||||||
Eq,
|
Eq,
|
||||||
Ne,
|
Ne,
|
||||||
@ -7,6 +8,7 @@ pub enum CmpType {
|
|||||||
Le,
|
Le,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum CmpCondType {
|
pub enum CmpCondType {
|
||||||
And,
|
And,
|
||||||
Or,
|
Or,
|
||||||
|
@ -195,6 +195,18 @@ mod tests {
|
|||||||
|
|
||||||
let jf = do_filter("$.friends[?( (@.id >= 2 || @.id == 1) && @.id == 0)]", "./benches/data_obj.json");
|
let jf = do_filter("$.friends[?( (@.id >= 2 || @.id == 1) && @.id == 0)]", "./benches/data_obj.json");
|
||||||
assert_eq!(&Value::Null, jf.current_value());
|
assert_eq!(&Value::Null, jf.current_value());
|
||||||
|
|
||||||
|
let jf = do_filter("$..friends[?(@.id == $.index)].id", "./benches/data_obj.json");
|
||||||
|
let friends = json!([0, 0]);
|
||||||
|
assert_eq!(&friends, jf.current_value());
|
||||||
|
|
||||||
|
let jf = do_filter("$..book[?($.store.bicycle.price < @.price)].price", "./benches/example.json");
|
||||||
|
let friends = json!([22.99]);
|
||||||
|
assert_eq!(&friends, jf.current_value());
|
||||||
|
|
||||||
|
let jf = do_filter("$..book[?( (@.price == 12.99 || @.category == 'reference') && @.price > 10)].price", "./benches/example.json");
|
||||||
|
let friends = json!([12.99]);
|
||||||
|
assert_eq!(&friends, jf.current_value());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use super::cmp::*;
|
use super::cmp::*;
|
||||||
use super::value_wrapper::*;
|
|
||||||
use super::value_filter::ValueFilterKey;
|
use super::value_filter::ValueFilterKey;
|
||||||
|
use super::value_wrapper::*;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum TermContext {
|
pub enum TermContext {
|
||||||
@ -17,17 +17,34 @@ impl TermContext {
|
|||||||
TermContext::Constants(ExprTerm::Bool(et.cmp(oet, cmp_fn, default)))
|
TermContext::Constants(ExprTerm::Bool(et.cmp(oet, cmp_fn, default)))
|
||||||
}
|
}
|
||||||
TermContext::Json(key, v) => {
|
TermContext::Json(key, v) => {
|
||||||
TermContext::Json(None, v.take_with(key, et, cmp_fn))
|
TermContext::Json(None, v.take_with(key, et, cmp_fn, true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TermContext::Json(key, v) => {
|
TermContext::Json(key, v) => {
|
||||||
match other {
|
match other {
|
||||||
TermContext::Json(_, ov) => {
|
TermContext::Json(key_other, ov) => {
|
||||||
v.cmp(ov, cmp_fn.into_type())
|
|
||||||
|
fn is_json(t: &TermContext) -> bool {
|
||||||
|
match t {
|
||||||
|
TermContext::Json(_, _) => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut v = v.filter(key);
|
||||||
|
let mut ov = ov.filter(key_other);
|
||||||
|
let mut c = v.into_term(key);
|
||||||
|
let mut oc = ov.into_term(key_other);
|
||||||
|
|
||||||
|
if is_json(&c) && is_json(&oc) {
|
||||||
|
v.cmp(&mut ov, cmp_fn.into_type())
|
||||||
|
} else {
|
||||||
|
c.cmp(&mut oc, cmp_fn, default)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
TermContext::Constants(et) => {
|
TermContext::Constants(et) => {
|
||||||
TermContext::Json(None, v.take_with(key, et, cmp_fn))
|
TermContext::Json(None, v.take_with(key, et, cmp_fn, false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ impl ArrayIndex for usize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ValueFilterKey {
|
pub enum ValueFilterKey {
|
||||||
Num(usize),
|
Num(usize),
|
||||||
String(String),
|
String(String),
|
||||||
@ -241,7 +241,7 @@ impl ValueFilter {
|
|||||||
|
|
||||||
self.last_key = Some(ValueFilterKey::String(key.clone()));
|
self.last_key = Some(ValueFilterKey::String(key.clone()));
|
||||||
self.vw.replace(v);
|
self.vw.replace(v);
|
||||||
trace!("step_in_string - after: {:?}", self.vw.get_val());
|
trace!("step_in_string - after: {},{},{:?}", self.vw.is_leaves(), self.filter_mode, self.vw.get_val());
|
||||||
&self.vw
|
&self.vw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -410,7 +410,9 @@ impl JsonValueFilter {
|
|||||||
Some(ParseToken::Leaves) => {
|
Some(ParseToken::Leaves) => {
|
||||||
vf.step_leaves_string(&key);
|
vf.step_leaves_string(&key);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {
|
||||||
|
self.term_stack.push(TermContext::Constants(ExprTerm::String(key)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use serde_json::Value;
|
|
||||||
use indexmap::map::IndexMap;
|
use indexmap::map::IndexMap;
|
||||||
|
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
use super::cmp::*;
|
use super::cmp::*;
|
||||||
use super::term::*;
|
use super::term::*;
|
||||||
use super::value_filter::*;
|
use super::value_filter::*;
|
||||||
@ -38,18 +39,18 @@ impl ValueWrapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cmp_with_term<F: PrivCmp>(val: &Value, et: &ExprTerm, cmp_fn: &F, default: bool) -> bool {
|
fn cmp_with_term<F: PrivCmp>(val: &Value, et: &ExprTerm, cmp_fn: &F, default: bool, reverse: bool) -> bool {
|
||||||
match val {
|
match val {
|
||||||
Value::Bool(ref v1) => {
|
Value::Bool(ref v1) => {
|
||||||
match et {
|
match et {
|
||||||
ExprTerm::Bool(v2) => cmp_fn.cmp_bool(v1, v2),
|
ExprTerm::Bool(v2) => if reverse { cmp_fn.cmp_bool(v2, v1) } else { cmp_fn.cmp_bool(v1, v2) },
|
||||||
_ => default
|
_ => default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::Number(ref v1) => match v1.as_f64() {
|
Value::Number(ref v1) => match v1.as_f64() {
|
||||||
Some(ref v1) => {
|
Some(ref v1) => {
|
||||||
match et {
|
match et {
|
||||||
ExprTerm::Number(v2) => cmp_fn.cmp_f64(v1, v2),
|
ExprTerm::Number(v2) => if reverse { cmp_fn.cmp_f64(v2, v1) } else { cmp_fn.cmp_f64(v1, v2) },
|
||||||
_ => default
|
_ => default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -57,7 +58,7 @@ impl ValueWrapper {
|
|||||||
},
|
},
|
||||||
Value::String(ref v1) => {
|
Value::String(ref v1) => {
|
||||||
match et {
|
match et {
|
||||||
ExprTerm::String(v2) => cmp_fn.cmp_string(v1, v2),
|
ExprTerm::String(v2) => if reverse { cmp_fn.cmp_string(v2, v1) } else { cmp_fn.cmp_string(v1, v2) },
|
||||||
_ => default
|
_ => default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -65,7 +66,7 @@ impl ValueWrapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_object_in_array<F: PrivCmp>(&mut self, key: &String, et: &ExprTerm, cmp: &F) -> Option<Self> {
|
fn take_object_in_array<F: PrivCmp>(&mut self, key: &String, et: &ExprTerm, cmp: &F, reverse: bool) -> Option<Self> {
|
||||||
fn _filter_with_object<F: Fn(&Value) -> bool>(v: &&mut Value, key: &String, fun: F) -> bool {
|
fn _filter_with_object<F: Fn(&Value) -> bool>(v: &&mut Value, key: &String, fun: F) -> bool {
|
||||||
match &v {
|
match &v {
|
||||||
Value::Object(map) => {
|
Value::Object(map) => {
|
||||||
@ -82,7 +83,7 @@ impl ValueWrapper {
|
|||||||
Value::Array(mut vec) => {
|
Value::Array(mut vec) => {
|
||||||
let mut ret: Vec<Value> = vec.iter_mut()
|
let mut ret: Vec<Value> = vec.iter_mut()
|
||||||
.filter(|v| {
|
.filter(|v| {
|
||||||
_filter_with_object(v, key, |vv| Self::cmp_with_term(vv, et, cmp, false))
|
_filter_with_object(v, key, |vv| Self::cmp_with_term(vv, et, cmp, false, reverse))
|
||||||
})
|
})
|
||||||
.map(|v| v.take())
|
.map(|v| v.take())
|
||||||
.collect();
|
.collect();
|
||||||
@ -92,29 +93,29 @@ impl ValueWrapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_with_key_type<F: PrivCmp>(&mut self, key: &Option<ValueFilterKey>, et: &ExprTerm, cmp: &F) -> Option<Self> {
|
fn take_with_key_type<F: PrivCmp>(&mut self, key: &Option<ValueFilterKey>, et: &ExprTerm, cmp: &F, reverse: bool) -> Option<Self> {
|
||||||
match key {
|
match key {
|
||||||
Some(ValueFilterKey::String(key)) => {
|
Some(ValueFilterKey::String(key)) => {
|
||||||
self.take_object_in_array(key, et, cmp)
|
self.take_object_in_array(key, et, cmp, reverse)
|
||||||
}
|
}
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn take_with<F: PrivCmp>(&mut self, key: &Option<ValueFilterKey>, et: &ExprTerm, cmp: F) -> Self {
|
pub fn take_with<F: PrivCmp>(&mut self, key: &Option<ValueFilterKey>, et: &ExprTerm, cmp: F, reverse: bool) -> Self {
|
||||||
match self.take_with_key_type(key, et, &cmp) {
|
match self.take_with_key_type(key, et, &cmp, reverse) {
|
||||||
Some(vw) => vw,
|
Some(vw) => vw,
|
||||||
_ => {
|
_ => {
|
||||||
match self.val.take() {
|
match self.val.take() {
|
||||||
Value::Array(mut vec) => {
|
Value::Array(mut vec) => {
|
||||||
let mut ret = vec.iter_mut()
|
let mut ret = vec.iter_mut()
|
||||||
.filter(|v| Self::cmp_with_term(&v, et, &cmp, false))
|
.filter(|v| Self::cmp_with_term(&v, et, &cmp, false, reverse))
|
||||||
.map(|v| v.take())
|
.map(|v| v.take())
|
||||||
.collect();
|
.collect();
|
||||||
ValueWrapper::new(Value::Array(ret), false)
|
ValueWrapper::new(Value::Array(ret), false)
|
||||||
}
|
}
|
||||||
other => {
|
other => {
|
||||||
if Self::cmp_with_term(&other, et, &cmp, false) {
|
if Self::cmp_with_term(&other, et, &cmp, false, reverse) {
|
||||||
ValueWrapper::new(other, false)
|
ValueWrapper::new(other, false)
|
||||||
} else {
|
} else {
|
||||||
ValueWrapper::new(Value::Null, false)
|
ValueWrapper::new(Value::Null, false)
|
||||||
@ -257,4 +258,45 @@ impl ValueWrapper {
|
|||||||
vw.replace(Value::Array(list));
|
vw.replace(Value::Array(list));
|
||||||
vw
|
vw
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_term(&mut self, key: &mut Option<ValueFilterKey>) -> TermContext {
|
||||||
|
match self.val.take() {
|
||||||
|
Value::String(s) => TermContext::Constants(ExprTerm::String(s)),
|
||||||
|
Value::Number(n) => TermContext::Constants(ExprTerm::Number(n.as_f64().unwrap())),
|
||||||
|
Value::Bool(b) => TermContext::Constants(ExprTerm::Bool(b)),
|
||||||
|
other => TermContext::Json(match key {
|
||||||
|
Some(vk) => Some(vk.clone()),
|
||||||
|
_ => None
|
||||||
|
}, ValueWrapper::new(other, false))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn filter(&mut self, key: &mut Option<ValueFilterKey>) -> Self {
|
||||||
|
let v = match &mut self.val {
|
||||||
|
Value::Array(vec) => {
|
||||||
|
let ret = vec.iter_mut()
|
||||||
|
.filter(|v| match key {
|
||||||
|
Some(ValueFilterKey::String(val_key)) => {
|
||||||
|
v.get(val_key.as_str()).is_some()
|
||||||
|
}
|
||||||
|
_ => false
|
||||||
|
})
|
||||||
|
.map(|v| v.take())
|
||||||
|
.collect();
|
||||||
|
Value::Array(ret)
|
||||||
|
}
|
||||||
|
Value::Object(map) => {
|
||||||
|
match key {
|
||||||
|
Some(ValueFilterKey::String(val_key)) => match map.get_mut(val_key) {
|
||||||
|
Some(v) => v.take(),
|
||||||
|
_ => Value::Null
|
||||||
|
},
|
||||||
|
_ => Value::Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
other => other.take()
|
||||||
|
};
|
||||||
|
|
||||||
|
ValueWrapper::new(v, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user