mirror of
https://github.com/fluencelabs/jsonpath
synced 2025-04-25 09:22:19 +00:00
JsonPath Example 테스트
This commit is contained in:
parent
76a5c27347
commit
0534dad755
@ -8,6 +8,7 @@ log = "0.4"
|
||||
env_logger = "0.6.0"
|
||||
serde = "1.0"
|
||||
serde_json = { version = "1.0", features = ["preserve_order"] }
|
||||
indexmap = "1.0.2"
|
||||
|
||||
[dev-dependencies]
|
||||
bencher = "0.1.5"
|
||||
|
116
benches/giveme_every_thing_result.json
Normal file
116
benches/giveme_every_thing_result.json
Normal file
@ -0,0 +1,116 @@
|
||||
[
|
||||
{
|
||||
"book": [
|
||||
{
|
||||
"category": "reference",
|
||||
"author": "Nigel Rees",
|
||||
"title": "Sayings of the Century",
|
||||
"price": 8.95
|
||||
},
|
||||
{
|
||||
"category": "fiction",
|
||||
"author": "Evelyn Waugh",
|
||||
"title": "Sword of Honour",
|
||||
"price": 12.99
|
||||
},
|
||||
{
|
||||
"category": "fiction",
|
||||
"author": "Herman Melville",
|
||||
"title": "Moby Dick",
|
||||
"isbn": "0-553-21311-3",
|
||||
"price": 8.99
|
||||
},
|
||||
{
|
||||
"category": "fiction",
|
||||
"author": "J. R. R. Tolkien",
|
||||
"title": "The Lord of the Rings",
|
||||
"isbn": "0-395-19395-8",
|
||||
"price": 22.99
|
||||
}
|
||||
],
|
||||
"bicycle": {
|
||||
"color": "red",
|
||||
"price": 19.95
|
||||
}
|
||||
},
|
||||
10,
|
||||
[
|
||||
{
|
||||
"category": "reference",
|
||||
"author": "Nigel Rees",
|
||||
"title": "Sayings of the Century",
|
||||
"price": 8.95
|
||||
},
|
||||
{
|
||||
"category": "fiction",
|
||||
"author": "Evelyn Waugh",
|
||||
"title": "Sword of Honour",
|
||||
"price": 12.99
|
||||
},
|
||||
{
|
||||
"category": "fiction",
|
||||
"author": "Herman Melville",
|
||||
"title": "Moby Dick",
|
||||
"isbn": "0-553-21311-3",
|
||||
"price": 8.99
|
||||
},
|
||||
{
|
||||
"category": "fiction",
|
||||
"author": "J. R. R. Tolkien",
|
||||
"title": "The Lord of the Rings",
|
||||
"isbn": "0-395-19395-8",
|
||||
"price": 22.99
|
||||
}
|
||||
],
|
||||
{
|
||||
"color": "red",
|
||||
"price": 19.95
|
||||
},
|
||||
{
|
||||
"category": "reference",
|
||||
"author": "Nigel Rees",
|
||||
"title": "Sayings of the Century",
|
||||
"price": 8.95
|
||||
},
|
||||
{
|
||||
"category": "fiction",
|
||||
"author": "Evelyn Waugh",
|
||||
"title": "Sword of Honour",
|
||||
"price": 12.99
|
||||
},
|
||||
{
|
||||
"category": "fiction",
|
||||
"author": "Herman Melville",
|
||||
"title": "Moby Dick",
|
||||
"isbn": "0-553-21311-3",
|
||||
"price": 8.99
|
||||
},
|
||||
{
|
||||
"category": "fiction",
|
||||
"author": "J. R. R. Tolkien",
|
||||
"title": "The Lord of the Rings",
|
||||
"isbn": "0-395-19395-8",
|
||||
"price": 22.99
|
||||
},
|
||||
"reference",
|
||||
"Nigel Rees",
|
||||
"Sayings of the Century",
|
||||
8.95,
|
||||
"fiction",
|
||||
"Evelyn Waugh",
|
||||
"Sword of Honour",
|
||||
12.99,
|
||||
"fiction",
|
||||
"Herman Melville",
|
||||
"Moby Dick",
|
||||
"0-553-21311-3",
|
||||
8.99,
|
||||
"fiction",
|
||||
"J. R. R. Tolkien",
|
||||
"The Lord of the Rings",
|
||||
"0-395-19395-8",
|
||||
22.99,
|
||||
"red",
|
||||
19.95
|
||||
]
|
||||
|
File diff suppressed because it is too large
Load Diff
189
src/jsonpath/json_filter/cmp.rs
Normal file
189
src/jsonpath/json_filter/cmp.rs
Normal file
@ -0,0 +1,189 @@
|
||||
pub enum CmpType {
|
||||
Eq,
|
||||
Ne,
|
||||
Gt,
|
||||
Ge,
|
||||
Lt,
|
||||
Le,
|
||||
}
|
||||
|
||||
pub enum CmpCondType {
|
||||
And,
|
||||
Or,
|
||||
}
|
||||
|
||||
pub trait PrivCmp {
|
||||
fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool;
|
||||
|
||||
fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool;
|
||||
|
||||
fn cmp_string(&self, v1: &String, v2: &String) -> bool;
|
||||
}
|
||||
|
||||
pub trait IntoType {
|
||||
fn into_type(&self) -> CmpType;
|
||||
}
|
||||
|
||||
pub struct CmpEq;
|
||||
|
||||
impl PrivCmp for CmpEq {
|
||||
fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool {
|
||||
v1 == v2
|
||||
}
|
||||
|
||||
fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool {
|
||||
v1 == v2
|
||||
}
|
||||
|
||||
fn cmp_string(&self, v1: &String, v2: &String) -> bool {
|
||||
v1 == v2
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoType for CmpEq {
|
||||
fn into_type(&self) -> CmpType {
|
||||
CmpType::Eq
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CmpNe;
|
||||
|
||||
impl PrivCmp for CmpNe {
|
||||
fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool {
|
||||
v1 != v2
|
||||
}
|
||||
|
||||
fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool {
|
||||
v1 != v2
|
||||
}
|
||||
|
||||
fn cmp_string(&self, v1: &String, v2: &String) -> bool {
|
||||
v1 != v2
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoType for CmpNe {
|
||||
fn into_type(&self) -> CmpType {
|
||||
CmpType::Ne
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CmpGt;
|
||||
|
||||
impl PrivCmp for CmpGt {
|
||||
fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool {
|
||||
v1 > v2
|
||||
}
|
||||
|
||||
fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool {
|
||||
v1 > v2
|
||||
}
|
||||
|
||||
fn cmp_string(&self, v1: &String, v2: &String) -> bool {
|
||||
v1 > v2
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoType for CmpGt {
|
||||
fn into_type(&self) -> CmpType {
|
||||
CmpType::Gt
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CmpGe;
|
||||
|
||||
impl PrivCmp for CmpGe {
|
||||
fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool {
|
||||
v1 >= v2
|
||||
}
|
||||
|
||||
fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool {
|
||||
v1 >= v2
|
||||
}
|
||||
|
||||
fn cmp_string(&self, v1: &String, v2: &String) -> bool {
|
||||
v1 >= v2
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoType for CmpGe {
|
||||
fn into_type(&self) -> CmpType {
|
||||
CmpType::Ge
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CmpLt;
|
||||
|
||||
impl PrivCmp for CmpLt {
|
||||
fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool {
|
||||
v1 < v2
|
||||
}
|
||||
|
||||
fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool {
|
||||
v1 < v2
|
||||
}
|
||||
|
||||
fn cmp_string(&self, v1: &String, v2: &String) -> bool {
|
||||
v1 < v2
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoType for CmpLt {
|
||||
fn into_type(&self) -> CmpType {
|
||||
CmpType::Lt
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CmpLe;
|
||||
|
||||
impl PrivCmp for CmpLe {
|
||||
fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool {
|
||||
v1 <= v2
|
||||
}
|
||||
|
||||
fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool {
|
||||
v1 <= v2
|
||||
}
|
||||
|
||||
fn cmp_string(&self, v1: &String, v2: &String) -> bool {
|
||||
v1 <= v2
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoType for CmpLe {
|
||||
fn into_type(&self) -> CmpType {
|
||||
CmpType::Le
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CmpAnd;
|
||||
|
||||
impl PrivCmp for CmpAnd {
|
||||
fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool {
|
||||
*v1 && *v2
|
||||
}
|
||||
|
||||
fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool {
|
||||
v1 > &0_f64 && v2 > &0_f64
|
||||
}
|
||||
|
||||
fn cmp_string(&self, v1: &String, v2: &String) -> bool {
|
||||
!v1.is_empty() && !v2.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CmpOr;
|
||||
|
||||
impl PrivCmp for CmpOr {
|
||||
fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool {
|
||||
*v1 || *v2
|
||||
}
|
||||
|
||||
fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool {
|
||||
v1 > &0_f64 || v2 > &0_f64
|
||||
}
|
||||
|
||||
fn cmp_string(&self, v1: &String, v2: &String) -> bool {
|
||||
!v1.is_empty() || !v2.is_empty()
|
||||
}
|
||||
}
|
330
src/jsonpath/json_filter/mod.rs
Normal file
330
src/jsonpath/json_filter/mod.rs
Normal file
@ -0,0 +1,330 @@
|
||||
mod cmp;
|
||||
mod term;
|
||||
mod value_filter;
|
||||
mod value_wrapper;
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
extern crate env_logger;
|
||||
|
||||
use std::io::Read;
|
||||
use std::sync::{Once, ONCE_INIT};
|
||||
|
||||
use jsonpath::parser::Parser;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
use super::value_filter::*;
|
||||
|
||||
static INIT: Once = ONCE_INIT;
|
||||
|
||||
fn setup() {
|
||||
INIT.call_once(|| {
|
||||
env_logger::init();
|
||||
});
|
||||
}
|
||||
|
||||
fn new_value_filter(file: &str) -> ValueFilter {
|
||||
let string = read_json(file);
|
||||
let json: Value = serde_json::from_str(string.as_str()).unwrap();
|
||||
ValueFilter::new(json, false, false)
|
||||
}
|
||||
|
||||
fn do_filter(path: &str, file: &str) -> JsonValueFilter {
|
||||
let string = read_json(file);
|
||||
let mut jf = JsonValueFilter::new(string.as_str()).unwrap();
|
||||
let mut parser = Parser::new(path);
|
||||
parser.parse(&mut jf).unwrap();
|
||||
jf
|
||||
}
|
||||
|
||||
fn read_json(path: &str) -> String {
|
||||
let mut f = std::fs::File::open(path).unwrap();
|
||||
let mut contents = String::new();
|
||||
f.read_to_string(&mut contents).unwrap();
|
||||
contents
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn step_in() {
|
||||
setup();
|
||||
|
||||
let mut jf = new_value_filter("./benches/data_obj.json");
|
||||
{
|
||||
let current = jf.step_in_str("friends");
|
||||
assert_eq!(current.is_array(), true);
|
||||
}
|
||||
|
||||
let mut jf = new_value_filter("./benches/data_array.json");
|
||||
{
|
||||
let current = jf.step_in_num(&1.0);
|
||||
assert_eq!(current.get_val().is_object(), true);
|
||||
}
|
||||
{
|
||||
let current = jf.step_in_str("friends");
|
||||
assert_eq!(current.is_array(), true);
|
||||
}
|
||||
let mut jf = new_value_filter("./benches/data_obj.json");
|
||||
{
|
||||
jf.step_in_str("school");
|
||||
jf.step_in_str("friends");
|
||||
jf.step_in_all();
|
||||
let current = jf.step_in_str("name");
|
||||
let friends = json!([
|
||||
"Millicent Norman",
|
||||
"Vincent Cannon",
|
||||
"Gray Berry"
|
||||
]);
|
||||
assert_eq!(&friends, current.get_val());
|
||||
}
|
||||
let mut jf = new_value_filter("./benches/data_obj.json");
|
||||
{
|
||||
let current = jf.step_leaves_str("name");
|
||||
let names = json!([
|
||||
"Leonor Herman",
|
||||
"Millicent Norman",
|
||||
"Vincent Cannon",
|
||||
"Gray Berry",
|
||||
"Vincent Cannon",
|
||||
"Gray Berry"
|
||||
]);
|
||||
assert_eq!(&names, current.get_val());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn array() {
|
||||
setup();
|
||||
|
||||
let friends = json!([
|
||||
{"id": 1, "name": "Vincent Cannon" },
|
||||
{"id": 2, "name": "Gray Berry"}
|
||||
]);
|
||||
|
||||
let jf = do_filter("$.school.friends[1, 2]", "./benches/data_obj.json");
|
||||
assert_eq!(&friends, jf.current_value());
|
||||
|
||||
let jf = do_filter("$.school.friends[1:]", "./benches/data_obj.json");
|
||||
assert_eq!(&friends, jf.current_value());
|
||||
|
||||
let jf = do_filter("$.school.friends[:-2]", "./benches/data_obj.json");
|
||||
let friends = json!([
|
||||
{"id": 0, "name": "Millicent Norman"}
|
||||
]);
|
||||
assert_eq!(&friends, jf.current_value());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn return_type() {
|
||||
setup();
|
||||
|
||||
let friends = json!({
|
||||
"friends": [
|
||||
{"id": 0, "name": "Millicent Norman"},
|
||||
{"id": 1, "name": "Vincent Cannon" },
|
||||
{"id": 2, "name": "Gray Berry"}
|
||||
]
|
||||
});
|
||||
|
||||
let jf = do_filter("$.school", "./benches/data_obj.json");
|
||||
assert_eq!(&friends, jf.current_value());
|
||||
|
||||
let jf = do_filter("$.school[?(@.friends[0])]", "./benches/data_obj.json");
|
||||
assert_eq!(&friends, jf.current_value());
|
||||
|
||||
let jf = do_filter("$.school[?(@.friends[10])]", "./benches/data_obj.json");
|
||||
assert_eq!(&Value::Null, jf.current_value());
|
||||
|
||||
let jf = do_filter("$.school[?(1==1)]", "./benches/data_obj.json");
|
||||
assert_eq!(&friends, jf.current_value());
|
||||
|
||||
let jf = do_filter("$.school.friends[?(1==1)]", "./benches/data_obj.json");
|
||||
let friends = json!([
|
||||
{"id": 0, "name": "Millicent Norman"},
|
||||
{"id": 1, "name": "Vincent Cannon" },
|
||||
{"id": 2, "name": "Gray Berry"}
|
||||
]);
|
||||
assert_eq!(&friends, jf.current_value());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn op() {
|
||||
setup();
|
||||
|
||||
let jf = do_filter("$.school[?(@.friends == @.friends)]", "./benches/data_obj.json");
|
||||
let friends = json!({
|
||||
"friends": [
|
||||
{"id": 0, "name": "Millicent Norman"},
|
||||
{"id": 1, "name": "Vincent Cannon" },
|
||||
{"id": 2, "name": "Gray Berry"}
|
||||
]
|
||||
});
|
||||
assert_eq!(&friends, jf.current_value());
|
||||
|
||||
let jf = do_filter("$.friends[?(@.name)]", "./benches/data_obj.json");
|
||||
let friends = json!([
|
||||
{ "id" : 1, "name" : "Vincent Cannon" },
|
||||
{ "id" : 2, "name" : "Gray Berry" }
|
||||
]);
|
||||
assert_eq!(&friends, jf.current_value());
|
||||
|
||||
let jf = do_filter("$.friends[?(@.id >= 2)]", "./benches/data_obj.json");
|
||||
let friends = json!([
|
||||
{ "id" : 2, "name" : "Gray Berry" }
|
||||
]);
|
||||
assert_eq!(&friends, jf.current_value());
|
||||
|
||||
let jf = do_filter("$.friends[?(@.id >= 2 || @.id == 1)]", "./benches/data_obj.json");
|
||||
let friends = json!([
|
||||
{ "id" : 2, "name" : "Gray Berry" },
|
||||
{ "id" : 1, "name" : "Vincent Cannon" }
|
||||
]);
|
||||
assert_eq!(&friends, jf.current_value());
|
||||
|
||||
let jf = do_filter("$.friends[?( (@.id >= 2 || @.id == 1) && @.id == 0)]", "./benches/data_obj.json");
|
||||
assert_eq!(&Value::Null, jf.current_value());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn example() {
|
||||
setup();
|
||||
|
||||
let jf = do_filter("$.store.book[*].author", "./benches/example.json");
|
||||
let ret = json!(["Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien"]);
|
||||
assert_eq!(&ret, jf.current_value());
|
||||
|
||||
let jf = do_filter("$..author", "./benches/example.json");
|
||||
assert_eq!(&ret, jf.current_value());
|
||||
|
||||
let jf = do_filter("$.store.*", "./benches/example.json");
|
||||
let ret = json!([
|
||||
[
|
||||
{"category" : "reference", "author" : "Nigel Rees","title" : "Sayings of the Century", "price" : 8.95},
|
||||
{"category" : "fiction", "author" : "Evelyn Waugh","title" : "Sword of Honour","price" : 12.99},
|
||||
{"category" : "fiction", "author" : "Herman Melville","title" : "Moby Dick","isbn" : "0-553-21311-3","price" : 8.99},
|
||||
{"category" : "fiction", "author" : "J. R. R. Tolkien","title" : "The Lord of the Rings","isbn" : "0-395-19395-8","price" : 22.99}
|
||||
],
|
||||
{"color" : "red","price" : 19.95},
|
||||
]);
|
||||
assert_eq!(&ret, jf.current_value());
|
||||
|
||||
let jf = do_filter("$.store..price", "./benches/example.json");
|
||||
let ret = json!([8.95, 12.99, 8.99, 22.99, 19.95]);
|
||||
assert_eq!(&ret, jf.current_value());
|
||||
|
||||
let jf = do_filter("$..book[2]", "./benches/example.json");
|
||||
let ret = json!([{
|
||||
"category" : "fiction",
|
||||
"author" : "Herman Melville",
|
||||
"title" : "Moby Dick",
|
||||
"isbn" : "0-553-21311-3",
|
||||
"price" : 8.99
|
||||
}]);
|
||||
assert_eq!(&ret, jf.current_value());
|
||||
|
||||
let jf = do_filter("$..book[-2]", "./benches/example.json");
|
||||
let ret = json!([{
|
||||
"category" : "fiction",
|
||||
"author" : "Herman Melville",
|
||||
"title" : "Moby Dick",
|
||||
"isbn" : "0-553-21311-3",
|
||||
"price" : 8.99
|
||||
}]);
|
||||
assert_eq!(&ret, jf.current_value());
|
||||
|
||||
let jf = do_filter("$..book[0,1]", "./benches/example.json");
|
||||
let ret = json!([
|
||||
{
|
||||
"category" : "reference",
|
||||
"author" : "Nigel Rees",
|
||||
"title" : "Sayings of the Century",
|
||||
"price" : 8.95
|
||||
},
|
||||
{
|
||||
"category" : "fiction",
|
||||
"author" : "Evelyn Waugh",
|
||||
"title" : "Sword of Honour",
|
||||
"price" : 12.99
|
||||
}
|
||||
]);
|
||||
assert_eq!(&ret, jf.current_value());
|
||||
|
||||
let jf = do_filter("$..book[:2]", "./benches/example.json");
|
||||
let ret = json!([
|
||||
{
|
||||
"category" : "reference",
|
||||
"author" : "Nigel Rees",
|
||||
"title" : "Sayings of the Century",
|
||||
"price" : 8.95
|
||||
},
|
||||
{
|
||||
"category" : "fiction",
|
||||
"author" : "Evelyn Waugh",
|
||||
"title" : "Sword of Honour",
|
||||
"price" : 12.99
|
||||
}
|
||||
]);
|
||||
assert_eq!(&ret, jf.current_value());
|
||||
|
||||
let jf = do_filter("$..book[2:]", "./benches/example.json");
|
||||
let ret = json!([
|
||||
{
|
||||
"category" : "fiction",
|
||||
"author" : "Herman Melville",
|
||||
"title" : "Moby Dick",
|
||||
"isbn" : "0-553-21311-3",
|
||||
"price" : 8.99
|
||||
},
|
||||
{
|
||||
"category" : "fiction",
|
||||
"author" : "J. R. R. Tolkien",
|
||||
"title" : "The Lord of the Rings",
|
||||
"isbn" : "0-395-19395-8",
|
||||
"price" : 22.99
|
||||
}
|
||||
]);
|
||||
assert_eq!(&ret, jf.current_value());
|
||||
|
||||
let jf = do_filter("$..book[?(@.isbn)]", "./benches/example.json");
|
||||
let ret = json!([
|
||||
{
|
||||
"category" : "fiction",
|
||||
"author" : "Herman Melville",
|
||||
"title" : "Moby Dick",
|
||||
"isbn" : "0-553-21311-3",
|
||||
"price" : 8.99
|
||||
},
|
||||
{
|
||||
"category" : "fiction",
|
||||
"author" : "J. R. R. Tolkien",
|
||||
"title" : "The Lord of the Rings",
|
||||
"isbn" : "0-395-19395-8",
|
||||
"price" : 22.99
|
||||
}
|
||||
]);
|
||||
assert_eq!(&ret, jf.current_value());
|
||||
|
||||
let jf = do_filter("$.store.book[?(@.price < 10)]", "./benches/example.json");
|
||||
let ret = json!([
|
||||
{
|
||||
"category" : "reference",
|
||||
"author" : "Nigel Rees",
|
||||
"title" : "Sayings of the Century",
|
||||
"price" : 8.95
|
||||
},
|
||||
{
|
||||
"category" : "fiction",
|
||||
"author" : "Herman Melville",
|
||||
"title" : "Moby Dick",
|
||||
"isbn" : "0-553-21311-3",
|
||||
"price" : 8.99
|
||||
}
|
||||
]);
|
||||
assert_eq!(&ret, jf.current_value());
|
||||
|
||||
let jf = do_filter("$..*", "./benches/example.json");
|
||||
let json: Value = serde_json::from_str(read_json("./benches/giveme_every_thing_result.json").as_str()).unwrap();
|
||||
assert_eq!(&json, jf.current_value());
|
||||
}
|
||||
}
|
130
src/jsonpath/json_filter/term.rs
Normal file
130
src/jsonpath/json_filter/term.rs
Normal file
@ -0,0 +1,130 @@
|
||||
use super::cmp::*;
|
||||
use super::value_wrapper::*;
|
||||
use super::value_filter::ValueFilterKey;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TermContext {
|
||||
Constants(ExprTerm),
|
||||
Json(Option<ValueFilterKey>, ValueWrapper),
|
||||
}
|
||||
|
||||
impl TermContext {
|
||||
fn cmp<F: PrivCmp + IntoType>(&mut self, other: &mut TermContext, cmp_fn: F, default: bool) -> TermContext {
|
||||
match self {
|
||||
TermContext::Constants(et) => {
|
||||
match other {
|
||||
TermContext::Constants(oet) => {
|
||||
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(key, v) => {
|
||||
match other {
|
||||
TermContext::Json(_, ov) => {
|
||||
v.cmp(ov, cmp_fn.into_type())
|
||||
}
|
||||
TermContext::Constants(et) => {
|
||||
TermContext::Json(None, v.take_with(key, et, cmp_fn))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp_cond(&mut self, other: &mut TermContext, cmp_cond_type: CmpCondType) -> TermContext {
|
||||
match self {
|
||||
TermContext::Constants(et) => {
|
||||
match other {
|
||||
TermContext::Constants(oet) => {
|
||||
match cmp_cond_type {
|
||||
CmpCondType::Or => {
|
||||
TermContext::Constants(ExprTerm::Bool(et.cmp(oet, CmpOr, false)))
|
||||
}
|
||||
CmpCondType::And => {
|
||||
TermContext::Constants(ExprTerm::Bool(et.cmp(oet, CmpAnd, false)))
|
||||
}
|
||||
}
|
||||
}
|
||||
TermContext::Json(_, v) => {
|
||||
TermContext::Json(None, ValueWrapper::new(v.clone_val(), false))
|
||||
}
|
||||
}
|
||||
}
|
||||
TermContext::Json(_, v) => {
|
||||
match other {
|
||||
TermContext::Json(_, ov) => {
|
||||
match cmp_cond_type {
|
||||
CmpCondType::Or => TermContext::Json(None, v.union(ov)),
|
||||
CmpCondType::And => TermContext::Json(None, v.intersect(ov)),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
TermContext::Json(None, ValueWrapper::new(v.clone_val(), false))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq(&mut self, other: &mut TermContext) -> TermContext {
|
||||
self.cmp(other, CmpEq, false)
|
||||
}
|
||||
|
||||
pub fn ne(&mut self, other: &mut TermContext) -> TermContext {
|
||||
self.cmp(other, CmpNe, true)
|
||||
}
|
||||
|
||||
pub fn gt(&mut self, other: &mut TermContext) -> TermContext {
|
||||
self.cmp(other, CmpGt, false)
|
||||
}
|
||||
|
||||
pub fn ge(&mut self, other: &mut TermContext) -> TermContext {
|
||||
self.cmp(other, CmpGe, false)
|
||||
}
|
||||
|
||||
pub fn lt(&mut self, other: &mut TermContext) -> TermContext {
|
||||
self.cmp(other, CmpLt, false)
|
||||
}
|
||||
|
||||
pub fn le(&mut self, other: &mut TermContext) -> TermContext {
|
||||
self.cmp(other, CmpLe, false)
|
||||
}
|
||||
|
||||
pub fn and(&mut self, other: &mut TermContext) -> TermContext {
|
||||
self.cmp_cond(other, CmpCondType::And)
|
||||
}
|
||||
|
||||
pub fn or(&mut self, other: &mut TermContext) -> TermContext {
|
||||
self.cmp_cond(other, CmpCondType::Or)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ExprTerm {
|
||||
String(String),
|
||||
Number(f64),
|
||||
Bool(bool),
|
||||
}
|
||||
|
||||
impl ExprTerm {
|
||||
fn cmp<F: PrivCmp>(&self, other: &ExprTerm, cmp_fn: F, default: bool) -> bool {
|
||||
match self {
|
||||
ExprTerm::Bool(v1) => match other {
|
||||
ExprTerm::Bool(v2) => cmp_fn.cmp_bool(v1, v2),
|
||||
_ => default
|
||||
}
|
||||
ExprTerm::Number(v1) => match other {
|
||||
ExprTerm::Number(v2) => cmp_fn.cmp_f64(v1, v2),
|
||||
_ => default
|
||||
}
|
||||
ExprTerm::String(v1) => match other {
|
||||
ExprTerm::String(v2) => cmp_fn.cmp_string(v1, v2),
|
||||
_ => default
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
568
src/jsonpath/json_filter/value_filter.rs
Normal file
568
src/jsonpath/json_filter/value_filter.rs
Normal file
@ -0,0 +1,568 @@
|
||||
use core::borrow::Borrow;
|
||||
use std::error::Error;
|
||||
use std::rc::Rc;
|
||||
use std::result;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
use jsonpath::parser::*;
|
||||
|
||||
use super::term::*;
|
||||
use super::value_wrapper::*;
|
||||
|
||||
trait ArrayIndex {
|
||||
fn index(&self, v: &Value) -> usize;
|
||||
|
||||
fn take_value(&self, v: &mut Value) -> Value {
|
||||
let idx = self.index(v);
|
||||
match v.get_mut(idx) {
|
||||
Some(v) => v.take(),
|
||||
_ => Value::Null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ArrayIndex for f64 {
|
||||
fn index(&self, v: &Value) -> usize {
|
||||
if v.is_array() && self < &0_f64 {
|
||||
(v.as_array().unwrap().len() as f64 + self) as usize
|
||||
} else {
|
||||
*self as usize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ArrayIndex for isize {
|
||||
fn index(&self, v: &Value) -> usize {
|
||||
if v.is_array() && self < &0_isize {
|
||||
(v.as_array().unwrap().len() as isize + self) as usize
|
||||
} else {
|
||||
*self as usize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ArrayIndex for usize {
|
||||
fn index(&self, _: &Value) -> usize {
|
||||
*self as usize
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ValueFilterKey {
|
||||
Num(usize),
|
||||
String(String),
|
||||
All,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ValueFilter {
|
||||
vw: ValueWrapper,
|
||||
last_key: Option<ValueFilterKey>,
|
||||
filter_mode: bool,
|
||||
}
|
||||
|
||||
impl ValueFilter {
|
||||
pub fn new(v: Value, is_leaves: bool, filter_mode: bool) -> Self {
|
||||
ValueFilter { vw: ValueWrapper::new(v, is_leaves), last_key: None, filter_mode }
|
||||
}
|
||||
|
||||
fn iter_to_value_vec<'a, I: Iterator<Item=&'a mut Value>>(iter: I) -> Vec<Value> {
|
||||
iter.map(|v| v.take())
|
||||
.filter(|v| !v.is_null())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn get_nested_array<F: ArrayIndex>(v: &mut Value, key: F, filter_mode: bool) -> Value {
|
||||
if v.is_array() && v.as_array().unwrap().get(key.index(v)).is_some() {
|
||||
if filter_mode {
|
||||
v.take()
|
||||
} else {
|
||||
let idx = key.index(v);
|
||||
v.get_mut(idx).unwrap().take()
|
||||
}
|
||||
} else {
|
||||
key.take_value(v)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_nested_object(v: &mut Value, key: &String, filter_mode: bool) -> Value {
|
||||
if v.is_object() && v.as_object().unwrap().contains_key(key) {
|
||||
if filter_mode {
|
||||
v.take()
|
||||
} else {
|
||||
v.get_mut(key).unwrap().take()
|
||||
}
|
||||
} else {
|
||||
Value::Null
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_all(key: Option<&String>, v: &Value, buf: &mut Vec<Value>) {
|
||||
match v {
|
||||
Value::Array(vec) => {
|
||||
if key.is_none() {
|
||||
for v in vec {
|
||||
buf.push(v.clone());
|
||||
}
|
||||
}
|
||||
for i in vec {
|
||||
Self::collect_all(key, &i, buf);
|
||||
}
|
||||
}
|
||||
Value::Object(v) => {
|
||||
for (k, v) in v.into_iter() {
|
||||
if match key {
|
||||
Some(map_key) => map_key == k,
|
||||
_ => true
|
||||
} {
|
||||
buf.push(v.clone());
|
||||
}
|
||||
}
|
||||
for (_, v) in v.into_iter() {
|
||||
Self::collect_all(key, &v, buf);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn step_leaves_all(&mut self) -> &ValueWrapper {
|
||||
debug!("step_leaves_all");
|
||||
let mut buf = Vec::new();
|
||||
Self::collect_all(None, &self.vw.get_val(), &mut buf);
|
||||
trace!("step_leaves_all - {:?}", buf);
|
||||
self.last_key = Some(ValueFilterKey::All);
|
||||
self.vw = ValueWrapper::new(Value::Array(buf), true);
|
||||
&self.vw
|
||||
}
|
||||
|
||||
pub fn step_leaves_str(&mut self, key: &str) -> &ValueWrapper {
|
||||
self.step_leaves_string(&key.to_string())
|
||||
}
|
||||
|
||||
pub fn step_leaves_string(&mut self, key: &String) -> &ValueWrapper {
|
||||
debug!("step_leaves_string");
|
||||
let mut buf: Vec<Value> = Vec::new();
|
||||
Self::collect_all(Some(key), &self.vw.get_val(), &mut buf);
|
||||
trace!("step_leaves_string - {:?}", buf);
|
||||
self.last_key = Some(ValueFilterKey::String(key.clone()));
|
||||
self.vw = ValueWrapper::new(Value::Array(buf), true);
|
||||
&self.vw
|
||||
}
|
||||
|
||||
pub fn step_in_all(&mut self) -> &ValueWrapper {
|
||||
debug!("step_in_all");
|
||||
|
||||
let vec = match &mut self.vw.get_val_mut() {
|
||||
Value::Object(map) => Self::iter_to_value_vec(map.values_mut()),
|
||||
Value::Array(list) => Self::iter_to_value_vec(list.iter_mut()),
|
||||
Value::Null => Vec::new(),
|
||||
other => vec![other.take()]
|
||||
};
|
||||
|
||||
self.last_key = Some(ValueFilterKey::All);
|
||||
self.vw.replace(Value::Array(vec));
|
||||
trace!("step_in_all - {:?}", self.vw.get_val());
|
||||
&self.vw
|
||||
}
|
||||
|
||||
pub fn step_in_num(&mut self, key: &f64) -> &ValueWrapper {
|
||||
debug!("step_in_num");
|
||||
trace!("step_in_num - before: {} - {:?}", self.filter_mode, self.vw.get_val());
|
||||
|
||||
let v = if self.vw.is_leaves() {
|
||||
let filter_mode = self.filter_mode;
|
||||
match &mut self.vw.get_val_mut() {
|
||||
Value::Array(v) => {
|
||||
let vec: Vec<Value> = v.iter_mut()
|
||||
.map(|v| Self::get_nested_array(v, *key, filter_mode))
|
||||
.filter(|v| !v.is_null())
|
||||
.collect();
|
||||
Value::Array(vec)
|
||||
}
|
||||
other => key.take_value(other)
|
||||
}
|
||||
} else {
|
||||
key.take_value(self.vw.get_val_mut())
|
||||
};
|
||||
|
||||
self.last_key = Some(ValueFilterKey::Num(key.index(&v)));
|
||||
self.vw.replace(v);
|
||||
trace!("step_in_num - after: {:?}", self.vw.get_val());
|
||||
&self.vw
|
||||
}
|
||||
|
||||
pub fn step_in_str(&mut self, key: &str) -> &ValueWrapper {
|
||||
self.step_in_string(&key.to_string())
|
||||
}
|
||||
|
||||
pub fn step_in_string(&mut self, key: &String) -> &ValueWrapper {
|
||||
debug!("step_in_string");
|
||||
trace!("step_in_string - before: {},{},{:?}", self.vw.is_leaves(), self.filter_mode, self.vw.get_val());
|
||||
|
||||
let filter_mode = self.filter_mode;
|
||||
let is_leaves = self.vw.is_leaves();
|
||||
let v = match &mut self.vw.get_val_mut() {
|
||||
Value::Array(ref mut vec) if is_leaves => {
|
||||
let mut buf = Vec::new();
|
||||
for mut item in vec {
|
||||
if let Value::Array(v) = item {
|
||||
let mut ret: Vec<Value> = v.iter_mut()
|
||||
.map(|v| Self::get_nested_object(v, key, filter_mode))
|
||||
.filter(|v| !v.is_null())
|
||||
.collect();
|
||||
buf.append(&mut ret);
|
||||
}
|
||||
}
|
||||
|
||||
Value::Array(buf)
|
||||
}
|
||||
Value::Array(v) if !is_leaves => {
|
||||
let vec: Vec<Value> = v.iter_mut()
|
||||
.map(|v| Self::get_nested_object(v, key, filter_mode))
|
||||
.filter(|v| !v.is_null())
|
||||
.collect();
|
||||
|
||||
Value::Array(vec)
|
||||
}
|
||||
other => {
|
||||
match other.get_mut(key) {
|
||||
Some(v) => v.take(),
|
||||
_ => Value::Null
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.last_key = Some(ValueFilterKey::String(key.clone()));
|
||||
self.vw.replace(v);
|
||||
trace!("step_in_string - after: {:?}", self.vw.get_val());
|
||||
&self.vw
|
||||
}
|
||||
}
|
||||
|
||||
pub struct JsonValueFilter {
|
||||
json: Rc<Box<Value>>,
|
||||
filter_stack: Vec<ValueFilter>,
|
||||
token_stack: Vec<ParseToken>,
|
||||
term_stack: Vec<TermContext>,
|
||||
}
|
||||
|
||||
impl JsonValueFilter {
|
||||
pub fn new(json: &str) -> result::Result<Self, String> {
|
||||
let json: Value = serde_json::from_str(json)
|
||||
.map_err(|e| e.description().to_string())?;
|
||||
Ok(JsonValueFilter {
|
||||
json: Rc::new(Box::new(json)),
|
||||
filter_stack: Vec::new(),
|
||||
token_stack: Vec::new(),
|
||||
term_stack: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn is_peek_token_array(&self) -> bool {
|
||||
if let Some(ParseToken::Array) = self.token_stack.last() {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn push_value_filter(&mut self, from_current: bool) {
|
||||
if from_current {
|
||||
self.filter_stack.last()
|
||||
.map(|vf| {
|
||||
ValueFilter::new(vf.vw.clone_val(), vf.vw.is_leaves(), from_current)
|
||||
})
|
||||
.and_then(|vf| {
|
||||
Some(self.filter_stack.push(vf))
|
||||
});
|
||||
} else {
|
||||
let v: &Value = self.json.as_ref().borrow();
|
||||
self.filter_stack.push({
|
||||
ValueFilter::new(v.clone(), false, from_current)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn replace_filter_stack(&mut self, v: Value) {
|
||||
if self.filter_stack.is_empty() {
|
||||
self.filter_stack.push(ValueFilter::new(v, false, false));
|
||||
} else {
|
||||
match self.filter_stack.last_mut() {
|
||||
Some(vf) => {
|
||||
if v.is_null() {
|
||||
vf.vw.replace(v);
|
||||
} else if vf.vw.is_array() {
|
||||
vf.vw.replace(v);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_value(&self) -> &Value {
|
||||
match self.filter_stack.last() {
|
||||
Some(v) => &v.vw.get_val(),
|
||||
_ => &Value::Null
|
||||
}
|
||||
}
|
||||
|
||||
fn token_union<F: ArrayIndex>(&mut self, indices: Vec<F>) {
|
||||
self.token_stack.pop();
|
||||
|
||||
match self.filter_stack.last_mut() {
|
||||
Some(ref mut vf) if vf.vw.is_array() && vf.vw.is_leaves() => {
|
||||
if let Value::Array(mut val) = vf.vw.get_val_mut().take() {
|
||||
let mut ret = Vec::new();
|
||||
for mut v in &mut val {
|
||||
for i in &indices {
|
||||
let v = i.take_value(v);
|
||||
if !v.is_null() {
|
||||
ret.push(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
vf.vw.replace(Value::Array(ret));
|
||||
}
|
||||
}
|
||||
Some(ref mut vf) if vf.vw.is_array() && !vf.vw.is_leaves() => {
|
||||
let ret = indices.into_iter()
|
||||
.map(|i| i.take_value(vf.vw.get_val_mut()))
|
||||
.filter(|v| !v.is_null())
|
||||
.collect();
|
||||
vf.vw.replace(Value::Array(ret));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn token_range(&mut self, from: Option<isize>, to: Option<isize>) {
|
||||
self.token_stack.pop();
|
||||
|
||||
fn _from_to<F: ArrayIndex>(from: Option<F>, to: Option<F>, val: &Value) -> (usize, usize) {
|
||||
let from = match from {
|
||||
Some(v) => v.index(val),
|
||||
_ => 0
|
||||
};
|
||||
let to = match to {
|
||||
Some(v) => v.index(val),
|
||||
_ => if let Value::Array(v) = val { v.len() } else { 0 }
|
||||
};
|
||||
(from, to)
|
||||
}
|
||||
|
||||
fn _range(from: usize, to: usize, v: &mut Value) -> Vec<Value> {
|
||||
trace!("range - {}:{}", from, to);
|
||||
|
||||
(from..to).into_iter()
|
||||
.map(|i| i.take_value(v))
|
||||
.filter(|v| !v.is_null())
|
||||
.collect()
|
||||
}
|
||||
|
||||
match self.filter_stack.last_mut() {
|
||||
Some(ref mut vf) if vf.vw.is_array() && vf.vw.is_leaves() => {
|
||||
if let Value::Array(mut vec) = vf.vw.get_val_mut().take() {
|
||||
let mut buf = Vec::new();
|
||||
for mut item in &mut vec {
|
||||
let (from, to) = _from_to(from, to, item);
|
||||
let mut v: Vec<Value> = _range(from, to, item);
|
||||
buf.append(&mut v);
|
||||
}
|
||||
vf.vw.replace(Value::Array(buf));
|
||||
}
|
||||
}
|
||||
Some(ref mut vf) if vf.vw.is_array() && !vf.vw.is_leaves() => {
|
||||
let (from, to) = _from_to(from, to, vf.vw.get_val());
|
||||
let v: Vec<Value> = _range(from, to, vf.vw.get_val_mut());
|
||||
vf.vw.replace(Value::Array(v));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn token_key(&mut self, key: String) {
|
||||
match self.filter_stack.last_mut() {
|
||||
Some(vf) => {
|
||||
match self.token_stack.pop() {
|
||||
Some(ParseToken::In) => {
|
||||
vf.step_in_string(&key);
|
||||
}
|
||||
Some(ParseToken::Leaves) => {
|
||||
vf.step_leaves_string(&key);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn token_all(&mut self) {
|
||||
match self.filter_stack.last_mut() {
|
||||
Some(vf) => {
|
||||
match self.token_stack.pop() {
|
||||
Some(ParseToken::In) => {
|
||||
vf.step_in_all();
|
||||
}
|
||||
Some(ParseToken::Leaves) => {
|
||||
vf.step_leaves_all();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn token_end_array(&mut self) {
|
||||
trace!("array_eof - term_stack: {:?}", self.term_stack);
|
||||
trace!("array_eof - filter_stack: {:?}", self.filter_stack);
|
||||
|
||||
match self.term_stack.pop() {
|
||||
Some(TermContext::Constants(ExprTerm::Number(v))) => {
|
||||
match self.filter_stack.last_mut() {
|
||||
Some(vf) => {
|
||||
vf.step_in_num(&v);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Some(TermContext::Json(_, mut vw)) => {
|
||||
self.replace_filter_stack(vw.get_val_mut().take());
|
||||
}
|
||||
_ => {
|
||||
match self.filter_stack.pop() {
|
||||
Some(mut vf) => {
|
||||
match vf.vw.get_val_mut() {
|
||||
Value::Null | Value::Bool(false) => {
|
||||
self.replace_filter_stack(Value::Null);
|
||||
}
|
||||
other => {
|
||||
self.replace_filter_stack(other.take());
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn token_op(&mut self, ft: &FilterToken) {
|
||||
let right = self.term_stack.pop();
|
||||
let left = self.term_stack.pop();
|
||||
|
||||
trace!("left {:?}", left);
|
||||
trace!("right {:?}", right);
|
||||
|
||||
if left.is_some() && right.is_some() {
|
||||
let mut left = left.unwrap();
|
||||
let mut right = right.unwrap();
|
||||
|
||||
let tc = match ft {
|
||||
FilterToken::Equal => left.eq(&mut right),
|
||||
FilterToken::NotEqual => left.ne(&mut right),
|
||||
FilterToken::Greater => left.gt(&mut right),
|
||||
FilterToken::GreaterOrEqual => left.ge(&mut right),
|
||||
FilterToken::Little => left.lt(&mut right),
|
||||
FilterToken::LittleOrEqual => left.le(&mut right),
|
||||
FilterToken::And => left.and(&mut right),
|
||||
FilterToken::Or => left.or(&mut right),
|
||||
};
|
||||
self.term_stack.push(tc);
|
||||
}
|
||||
|
||||
trace!("filter - {:?}", self.term_stack)
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeVisitor for JsonValueFilter {
|
||||
fn visit_token(&mut self, token: ParseToken) {
|
||||
debug!("visit_token: {:?}", token);
|
||||
|
||||
match token {
|
||||
ParseToken::Absolute
|
||||
| ParseToken::Relative => {
|
||||
if self.is_peek_token_array() {
|
||||
self.token_stack.pop();
|
||||
}
|
||||
self.push_value_filter(ParseToken::Relative == token);
|
||||
}
|
||||
ParseToken::In
|
||||
| ParseToken::Leaves
|
||||
| ParseToken::Array => {
|
||||
self.token_stack.push(token);
|
||||
}
|
||||
ParseToken::ArrayEof => {
|
||||
self.token_end_array();
|
||||
}
|
||||
ParseToken::All => {
|
||||
self.token_all();
|
||||
}
|
||||
ParseToken::Key(key) => {
|
||||
self.token_key(key);
|
||||
}
|
||||
ParseToken::Filter(ref ft) => {
|
||||
self.token_op(ft);
|
||||
}
|
||||
ParseToken::Number(v) => {
|
||||
self.term_stack.push(TermContext::Constants(ExprTerm::Number(v)))
|
||||
}
|
||||
ParseToken::Range(from, to) => {
|
||||
self.token_range(from, to);
|
||||
}
|
||||
ParseToken::Union(v) => {
|
||||
self.token_union(v);
|
||||
}
|
||||
ParseToken::Eof => {
|
||||
debug!("visit_token eof");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn end_term(&mut self) {
|
||||
debug!("end_term");
|
||||
|
||||
if let Some(ParseToken::Array) = self.token_stack.last() {
|
||||
self.token_stack.pop();
|
||||
}
|
||||
|
||||
trace!("end_term - term_stack {:?}", self.term_stack);
|
||||
trace!("end_term - token_stack {:?}", self.token_stack);
|
||||
trace!("end_term - filter_stack {:?}", self.filter_stack);
|
||||
|
||||
if self.token_stack.is_empty() && self.filter_stack.len() > 1 {
|
||||
match self.filter_stack.pop() {
|
||||
Some(vf) => {
|
||||
self.term_stack.push(TermContext::Json(vf.last_key, vf.vw));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if match self.token_stack.last() {
|
||||
Some(ParseToken::Key(_))
|
||||
| Some(ParseToken::Number(_)) => true,
|
||||
_ => false
|
||||
} {
|
||||
match self.token_stack.pop() {
|
||||
Some(ParseToken::Key(ref v)) if v.eq_ignore_ascii_case("true") => {
|
||||
self.term_stack.push(TermContext::Constants(ExprTerm::Bool(true)))
|
||||
}
|
||||
Some(ParseToken::Key(ref v)) if v.eq_ignore_ascii_case("false") => {
|
||||
self.term_stack.push(TermContext::Constants(ExprTerm::Bool(false)))
|
||||
}
|
||||
Some(ParseToken::Key(v)) => {
|
||||
self.term_stack.push(TermContext::Constants(ExprTerm::String(v)))
|
||||
}
|
||||
Some(ParseToken::Number(v)) => {
|
||||
self.term_stack.push(TermContext::Constants(ExprTerm::Number(v)))
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
256
src/jsonpath/json_filter/value_wrapper.rs
Normal file
256
src/jsonpath/json_filter/value_wrapper.rs
Normal file
@ -0,0 +1,256 @@
|
||||
use serde_json::Value;
|
||||
use indexmap::map::IndexMap;
|
||||
|
||||
use super::cmp::*;
|
||||
use super::term::*;
|
||||
use super::value_filter::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ValueWrapper {
|
||||
val: Value,
|
||||
leaves: bool,
|
||||
}
|
||||
|
||||
impl ValueWrapper {
|
||||
pub fn new(val: Value, leaves: bool) -> Self {
|
||||
ValueWrapper { val, leaves }
|
||||
}
|
||||
|
||||
pub fn is_leaves(&self) -> bool {
|
||||
self.leaves
|
||||
}
|
||||
|
||||
pub fn cmp(&mut self, other: &mut ValueWrapper, cmp_type: CmpType) -> TermContext {
|
||||
match cmp_type {
|
||||
CmpType::Eq => {
|
||||
TermContext::Json(None, self.intersect(other))
|
||||
}
|
||||
CmpType::Ne => {
|
||||
TermContext::Json(None, self.except(other))
|
||||
}
|
||||
CmpType::Gt | CmpType::Ge | CmpType::Lt | CmpType::Le => {
|
||||
TermContext::Constants(ExprTerm::Bool(false))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp_with_term<F: PrivCmp>(val: &Value, et: &ExprTerm, cmp_fn: &F, default: bool) -> bool {
|
||||
match val {
|
||||
Value::Bool(ref v1) => {
|
||||
match et {
|
||||
ExprTerm::Bool(v2) => 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),
|
||||
_ => default
|
||||
}
|
||||
}
|
||||
_ => default
|
||||
},
|
||||
Value::String(ref v1) => {
|
||||
match et {
|
||||
ExprTerm::String(v2) => cmp_fn.cmp_string(v1, v2),
|
||||
_ => default
|
||||
}
|
||||
}
|
||||
_ => default
|
||||
}
|
||||
}
|
||||
|
||||
fn take_object_in_array<F: PrivCmp>(&mut self, key: &String, et: &ExprTerm, cmp: &F) -> Option<Self> {
|
||||
fn _filter_with_object<F: Fn(&Value) -> bool>(v: &&mut Value, key: &String, fun: F) -> bool {
|
||||
match &v {
|
||||
Value::Object(map) => {
|
||||
match map.get(key) {
|
||||
Some(vv) => fun(vv),
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
match self.val.take() {
|
||||
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))
|
||||
})
|
||||
.map(|v| v.take())
|
||||
.collect();
|
||||
Some(ValueWrapper::new(Value::Array(ret), false))
|
||||
}
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn take_with_key_type<F: PrivCmp>(&mut self, key: &Option<ValueFilterKey>, et: &ExprTerm, cmp: &F) -> Option<Self> {
|
||||
match key {
|
||||
Some(ValueFilterKey::String(key)) => {
|
||||
self.take_object_in_array(key, et, cmp)
|
||||
}
|
||||
_ => 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) {
|
||||
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))
|
||||
.map(|v| v.take())
|
||||
.collect();
|
||||
ValueWrapper::new(Value::Array(ret), false)
|
||||
}
|
||||
other => {
|
||||
if Self::cmp_with_term(&other, et, &cmp, false) {
|
||||
ValueWrapper::new(other, false)
|
||||
} else {
|
||||
ValueWrapper::new(Value::Null, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn replace(&mut self, val: Value) {
|
||||
let is_null = match &val {
|
||||
Value::Array(v) => if v.is_empty() { true } else { false },
|
||||
Value::Object(m) => if m.is_empty() { true } else { false },
|
||||
_ => val.is_null()
|
||||
};
|
||||
self.val = if is_null { Value::Null } else { val };
|
||||
}
|
||||
|
||||
pub fn get_val(&self) -> &Value {
|
||||
&self.val
|
||||
}
|
||||
|
||||
pub fn get_val_mut(&mut self) -> &mut Value {
|
||||
&mut self.val
|
||||
}
|
||||
|
||||
pub fn clone_val(&self) -> Value {
|
||||
self.val.clone()
|
||||
}
|
||||
|
||||
pub fn is_array(&self) -> bool {
|
||||
self.val.is_array()
|
||||
}
|
||||
|
||||
fn uuid(v: &Value) -> String {
|
||||
fn _fn(v: &Value) -> String {
|
||||
match v {
|
||||
Value::Null => "null".to_string(),
|
||||
Value::String(v) => v.to_string(),
|
||||
Value::Bool(v) => v.to_string(),
|
||||
Value::Number(v) => v.to_string(),
|
||||
Value::Array(v) => {
|
||||
v.iter().enumerate()
|
||||
.map(|(i, v)| { format!("{}{}", i, _fn(v)) })
|
||||
.collect()
|
||||
}
|
||||
Value::Object(v) => {
|
||||
v.into_iter().map(|(k, v)| { format!("{}{}", k, _fn(v)) }).collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
_fn(v)
|
||||
}
|
||||
|
||||
fn into_map(&mut self) -> IndexMap<String, Value> {
|
||||
let mut map = IndexMap::new();
|
||||
match &mut self.val {
|
||||
Value::Array(v1) => {
|
||||
for v in v1 {
|
||||
map.insert(Self::uuid(v), v.take());
|
||||
}
|
||||
}
|
||||
other => {
|
||||
map.insert(Self::uuid(other), other.take());
|
||||
}
|
||||
}
|
||||
map
|
||||
}
|
||||
|
||||
pub fn except(&mut self, other: &mut Self) -> Self {
|
||||
let map = self.into_map();
|
||||
let mut ret: IndexMap<String, Value> = IndexMap::new();
|
||||
match &mut other.val {
|
||||
Value::Array(v1) => {
|
||||
for v in v1 {
|
||||
let key = Self::uuid(v);
|
||||
if !map.contains_key(&key) {
|
||||
ret.insert(key, v.take());
|
||||
}
|
||||
}
|
||||
}
|
||||
other => {
|
||||
let key = Self::uuid(other);
|
||||
if !map.contains_key(&key) {
|
||||
ret.insert(key, other.take());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let v = ret.values_mut().into_iter().map(|v| v.take()).collect();
|
||||
ValueWrapper::new(v, false)
|
||||
}
|
||||
|
||||
pub fn intersect(&mut self, other: &mut Self) -> Self {
|
||||
let map = self.into_map();
|
||||
let mut ret: IndexMap<String, Value> = IndexMap::new();
|
||||
match &mut other.val {
|
||||
Value::Array(v1) => {
|
||||
for v in v1 {
|
||||
let key = Self::uuid(v);
|
||||
if map.contains_key(&key) {
|
||||
ret.insert(key, v.take());
|
||||
}
|
||||
}
|
||||
}
|
||||
other => {
|
||||
let key = Self::uuid(other);
|
||||
if map.contains_key(&key) {
|
||||
ret.insert(key, other.take());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let v = ret.values_mut().into_iter().map(|v| v.take()).collect();
|
||||
ValueWrapper::new(v, false)
|
||||
}
|
||||
|
||||
pub fn union(&mut self, other: &mut Self) -> Self {
|
||||
let mut map = self.into_map();
|
||||
match &mut other.val {
|
||||
Value::Array(v1) => {
|
||||
for v in v1 {
|
||||
let key = Self::uuid(v);
|
||||
if !map.contains_key(&key) {
|
||||
map.insert(key, v.take());
|
||||
}
|
||||
}
|
||||
}
|
||||
other => {
|
||||
let key = Self::uuid(other);
|
||||
if !map.contains_key(&key) {
|
||||
map.insert(key, other.take());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut vw = ValueWrapper::new(Value::Null, false);
|
||||
let list: Vec<Value> = map.values_mut().into_iter().map(|val| val.take()).collect();
|
||||
vw.replace(Value::Array(list));
|
||||
vw
|
||||
}
|
||||
}
|
@ -5,5 +5,5 @@ extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate serde_json;
|
||||
extern crate core;
|
||||
|
||||
extern crate indexmap;
|
||||
pub mod jsonpath;
|
Loading…
x
Reference in New Issue
Block a user