JsonPath Example 테스트

This commit is contained in:
freestrings 2019-02-25 16:43:46 +09:00
parent 76a5c27347
commit 0534dad755
9 changed files with 1591 additions and 1217 deletions

View File

@ -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"

View 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

View 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()
}
}

View 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());
}
}

View 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
}
}
}
}

View 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)))
}
_ => {}
}
}
}
}

View 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
}
}

View File

@ -5,5 +5,5 @@ extern crate serde;
#[macro_use]
extern crate serde_json;
extern crate core;
extern crate indexmap;
pub mod jsonpath;