Json to Json comparator 수정

This commit is contained in:
freestrings 2019-03-06 18:12:54 +09:00
parent abc541d303
commit cdf0446a69
6 changed files with 98 additions and 25 deletions

View File

@ -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[?(@.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 |
| $..book[?(@.price <= $['expensive'])] *(not yet supported)* | ~~All books in store that are not "expensive"~~ |
| $..book[?(@.author =~ /.*REES/i)] *(not yet supported)* | ~~All books matching regex (ignore case)~~ |
| <a href="https://freestrings.github.io/jsonpath/?path=$..*" target="_blank">$..*</a> | Give me every thing
| $..book.length() *(not yet supported)* | ~~The number of books~~ |
| <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 &#124; &#124; $.store.bicycle.price < @.price) &#124;&#124; @.category == "reference")]</a> | Complex filter
## With Rust (as library)

View File

@ -1,3 +1,4 @@
#[derive(Debug)]
pub enum CmpType {
Eq,
Ne,
@ -7,6 +8,7 @@ pub enum CmpType {
Le,
}
#[derive(Debug)]
pub enum CmpCondType {
And,
Or,

View File

@ -195,6 +195,18 @@ mod tests {
let jf = do_filter("$.friends[?( (@.id >= 2 || @.id == 1) && @.id == 0)]", "./benches/data_obj.json");
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]

View File

@ -1,6 +1,6 @@
use super::cmp::*;
use super::value_wrapper::*;
use super::value_filter::ValueFilterKey;
use super::value_wrapper::*;
#[derive(Debug)]
pub enum TermContext {
@ -17,17 +17,34 @@ impl TermContext {
TermContext::Constants(ExprTerm::Bool(et.cmp(oet, cmp_fn, default)))
}
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) => {
match other {
TermContext::Json(_, ov) => {
v.cmp(ov, cmp_fn.into_type())
TermContext::Json(key_other, ov) => {
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::Json(None, v.take_with(key, et, cmp_fn))
TermContext::Json(None, v.take_with(key, et, cmp_fn, false))
}
}
}

View File

@ -48,7 +48,7 @@ impl ArrayIndex for usize {
}
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum ValueFilterKey {
Num(usize),
String(String),
@ -241,7 +241,7 @@ impl ValueFilter {
self.last_key = Some(ValueFilterKey::String(key.clone()));
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
}
}
@ -410,7 +410,9 @@ impl JsonValueFilter {
Some(ParseToken::Leaves) => {
vf.step_leaves_string(&key);
}
_ => {}
_ => {
self.term_stack.push(TermContext::Constants(ExprTerm::String(key)));
}
}
}
_ => {}

View File

@ -1,6 +1,7 @@
use serde_json::Value;
use indexmap::map::IndexMap;
use serde_json::Value;
use super::cmp::*;
use super::term::*;
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 {
Value::Bool(ref v1) => {
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
}
}
Value::Number(ref v1) => match v1.as_f64() {
Some(ref v1) => {
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
}
}
@ -57,7 +58,7 @@ impl ValueWrapper {
},
Value::String(ref v1) => {
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
}
}
@ -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 {
match &v {
Value::Object(map) => {
@ -82,7 +83,7 @@ impl ValueWrapper {
Value::Array(mut vec) => {
let mut ret: Vec<Value> = vec.iter_mut()
.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())
.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 {
Some(ValueFilterKey::String(key)) => {
self.take_object_in_array(key, et, cmp)
self.take_object_in_array(key, et, cmp, reverse)
}
_ => None
}
}
pub fn take_with<F: PrivCmp>(&mut self, key: &Option<ValueFilterKey>, et: &ExprTerm, cmp: F) -> Self {
match self.take_with_key_type(key, et, &cmp) {
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, reverse) {
Some(vw) => vw,
_ => {
match self.val.take() {
Value::Array(mut vec) => {
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())
.collect();
ValueWrapper::new(Value::Array(ret), false)
}
other => {
if Self::cmp_with_term(&other, et, &cmp, false) {
if Self::cmp_with_term(&other, et, &cmp, false, reverse) {
ValueWrapper::new(other, false)
} else {
ValueWrapper::new(Value::Null, false)
@ -257,4 +258,45 @@ impl ValueWrapper {
vw.replace(Value::Array(list));
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)
}
}