Merge pull request #170 from Orange-OpenSource/fix/support-wildcard-in-jsonpath

Support wildcard in jsonpath
This commit is contained in:
Fabrice Reix 2021-02-15 11:41:06 +01:00 committed by GitHub
commit a7bf5bb144
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 86 additions and 3 deletions

File diff suppressed because one or more lines are too long

View File

@ -19,6 +19,7 @@ jsonpath "$.errors[0]" exists
jsonpath "$.errors[0]" isCollection jsonpath "$.errors[0]" isCollection
jsonpath "$.errors[0].id" equals "error1" jsonpath "$.errors[0].id" equals "error1"
jsonpath "$.errors[0]['id']" equals "error1" jsonpath "$.errors[0]['id']" equals "error1"
jsonpath "$.errors[*].id" includes "error1"
jsonpath "$.duration" equals 1.5 jsonpath "$.duration" equals 1.5
jsonpath "$.duration" lessThanOrEquals 2.0 jsonpath "$.duration" lessThanOrEquals 2.0
jsonpath "$.duration" lessThan 2 jsonpath "$.duration" lessThan 2

View File

@ -1 +1 @@
{"entries":[{"request":{"method":"GET","url":"http://localhost:8000/assert-json"},"response":{"version":"HTTP/1.0","status":200,"asserts":[{"query":{"type":"jsonpath","expr":"$.count"},"predicate":{"type":"equal","value":5}},{"query":{"type":"jsonpath","expr":"$.count"},"predicate":{"type":"equal","value":5.0}},{"query":{"type":"jsonpath","expr":"$.count"},"predicate":{"type":"greater","value":1}},{"query":{"type":"jsonpath","expr":"$.count"},"predicate":{"type":"greater","value":1.0}},{"query":{"type":"jsonpath","expr":"$.success"},"predicate":{"type":"equal","value":false}},{"query":{"type":"jsonpath","expr":"$.success"},"predicate":{"not":true,"type":"equal","value":null}},{"query":{"type":"jsonpath","expr":"$.success"},"predicate":{"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.success"},"predicate":{"type":"isBoolean"}},{"query":{"type":"jsonpath","expr":"$.errors"},"predicate":{"type":"count","value":2}},{"query":{"type":"jsonpath","expr":"$.errors"},"predicate":{"type":"isCollection"}},{"query":{"type":"jsonpath","expr":"$.warnings"},"predicate":{"type":"count","value":0}},{"query":{"type":"jsonpath","expr":"$.toto"},"predicate":{"not":true,"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.warnings"},"predicate":{"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.warnings"},"predicate":{"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.errors[0]"},"predicate":{"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.errors[0]"},"predicate":{"type":"isCollection"}},{"query":{"type":"jsonpath","expr":"$.errors[0].id"},"predicate":{"type":"equal","value":"error1"}},{"query":{"type":"jsonpath","expr":"$.errors[0]['id']"},"predicate":{"type":"equal","value":"error1"}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"type":"equal","value":1.5}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"type":"less-or-equal","value":2.0}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"type":"greater","value":2}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"type":"isFloat"}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"not":true,"type":"isInteger"}},{"query":{"type":"jsonpath","expr":"$.nullable"},"predicate":{"type":"equal","value":null}}],"body":{"type":"json","value":{"count":5,"success":false,"errors":[{"id":"error1"},{"id":"error2"}],"warnings":[],"duration":1.5,"tags":["test"],"nullable":null}}}},{"request":{"method":"GET","url":"http://localhost:8000/assert-json/index"},"response":{"version":"HTTP/1.0","status":200,"captures":[{"name":"index","query":{"type":"body"}}]}},{"request":{"method":"GET","url":"http://localhost:8000/assert-json"},"response":{"version":"HTTP/1.0","status":200,"asserts":[{"query":{"type":"jsonpath","expr":"$.errors[{{index}}].id"},"predicate":{"type":"equal","value":"error2"}},{"query":{"type":"jsonpath","expr":"$.tags"},"predicate":{"type":"include","value":"test"}},{"query":{"type":"jsonpath","expr":"$.tags"},"predicate":{"not":true,"type":"include","value":"prod"}},{"query":{"type":"jsonpath","expr":"$.tags"},"predicate":{"not":true,"type":"include","value":null}}]}},{"request":{"method":"GET","url":"http://localhost:8000/assert-json/list"},"response":{"version":"HTTP/1.0","status":200,"asserts":[{"query":{"type":"jsonpath","expr":"$"},"predicate":{"type":"count","value":2}},{"query":{"type":"jsonpath","expr":"$.[0].name"},"predicate":{"type":"equal","value":"Bob"}},{"query":{"type":"jsonpath","expr":"$[0].name"},"predicate":{"type":"equal","value":"Bob"}}]}}]} {"entries":[{"request":{"method":"GET","url":"http://localhost:8000/assert-json"},"response":{"version":"HTTP/1.0","status":200,"asserts":[{"query":{"type":"jsonpath","expr":"$.count"},"predicate":{"type":"equal","value":5}},{"query":{"type":"jsonpath","expr":"$.count"},"predicate":{"type":"equal","value":5.0}},{"query":{"type":"jsonpath","expr":"$.count"},"predicate":{"type":"greater","value":1}},{"query":{"type":"jsonpath","expr":"$.count"},"predicate":{"type":"greater","value":1.0}},{"query":{"type":"jsonpath","expr":"$.success"},"predicate":{"type":"equal","value":false}},{"query":{"type":"jsonpath","expr":"$.success"},"predicate":{"not":true,"type":"equal","value":null}},{"query":{"type":"jsonpath","expr":"$.success"},"predicate":{"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.success"},"predicate":{"type":"isBoolean"}},{"query":{"type":"jsonpath","expr":"$.errors"},"predicate":{"type":"count","value":2}},{"query":{"type":"jsonpath","expr":"$.errors"},"predicate":{"type":"isCollection"}},{"query":{"type":"jsonpath","expr":"$.warnings"},"predicate":{"type":"count","value":0}},{"query":{"type":"jsonpath","expr":"$.toto"},"predicate":{"not":true,"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.warnings"},"predicate":{"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.warnings"},"predicate":{"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.errors[0]"},"predicate":{"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.errors[0]"},"predicate":{"type":"isCollection"}},{"query":{"type":"jsonpath","expr":"$.errors[0].id"},"predicate":{"type":"equal","value":"error1"}},{"query":{"type":"jsonpath","expr":"$.errors[0]['id']"},"predicate":{"type":"equal","value":"error1"}},{"query":{"type":"jsonpath","expr":"$.errors[*].id"},"predicate":{"type":"include","value":"error1"}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"type":"equal","value":1.5}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"type":"less-or-equal","value":2.0}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"type":"greater","value":2}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"type":"isFloat"}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"not":true,"type":"isInteger"}},{"query":{"type":"jsonpath","expr":"$.nullable"},"predicate":{"type":"equal","value":null}}],"body":{"type":"json","value":{"count":5,"success":false,"errors":[{"id":"error1"},{"id":"error2"}],"warnings":[],"duration":1.5,"tags":["test"],"nullable":null}}}},{"request":{"method":"GET","url":"http://localhost:8000/assert-json/index"},"response":{"version":"HTTP/1.0","status":200,"captures":[{"name":"index","query":{"type":"body"}}]}},{"request":{"method":"GET","url":"http://localhost:8000/assert-json"},"response":{"version":"HTTP/1.0","status":200,"asserts":[{"query":{"type":"jsonpath","expr":"$.errors[{{index}}].id"},"predicate":{"type":"equal","value":"error2"}},{"query":{"type":"jsonpath","expr":"$.tags"},"predicate":{"type":"include","value":"test"}},{"query":{"type":"jsonpath","expr":"$.tags"},"predicate":{"not":true,"type":"include","value":"prod"}},{"query":{"type":"jsonpath","expr":"$.tags"},"predicate":{"not":true,"type":"include","value":null}}]}},{"request":{"method":"GET","url":"http://localhost:8000/assert-json/list"},"response":{"version":"HTTP/1.0","status":200,"asserts":[{"query":{"type":"jsonpath","expr":"$"},"predicate":{"type":"count","value":2}},{"query":{"type":"jsonpath","expr":"$.[0].name"},"predicate":{"type":"equal","value":"Bob"}},{"query":{"type":"jsonpath","expr":"$[0].name"},"predicate":{"type":"equal","value":"Bob"}}]}}]}

View File

@ -28,6 +28,7 @@ pub struct Query {
pub enum Selector { pub enum Selector {
NameChild(String), NameChild(String),
ArrayIndex(usize), ArrayIndex(usize),
ArrayWildcard,
Filter(Predicate), Filter(Predicate),
RecursiveKey(String), RecursiveKey(String),
} }

View File

@ -45,6 +45,15 @@ impl Selector {
None => vec![], None => vec![],
Some(value) => vec![value.clone()], Some(value) => vec![value.clone()],
}, },
Selector::ArrayWildcard {} => {
let mut elements = vec![];
if let serde_json::Value::Array(values) = root {
for value in values {
elements.push(value);
}
}
elements
}
Selector::Filter(predicate) => match root { Selector::Filter(predicate) => match root {
serde_json::Value::Array(elements) => elements serde_json::Value::Array(elements) => elements
.iter() .iter()
@ -238,6 +247,46 @@ mod tests {
json!("J. R. R. Tolkien") json!("J. R. R. Tolkien")
] ]
); );
// $.store.book[*].author
let query = Query {
selectors: vec![
Selector::NameChild("store".to_string()),
Selector::NameChild("book".to_string()),
Selector::ArrayWildcard {},
Selector::NameChild("author".to_string()),
],
};
assert_eq!(
query.eval(json_root()),
vec![
json!("Nigel Rees"),
json!("Evelyn Waugh"),
json!("Herman Melville"),
json!("J. R. R. Tolkien")
]
);
}
#[test]
pub fn test_selector_array_index() {
assert_eq!(
Selector::ArrayIndex(0).eval(json_books()),
vec![json_first_book()]
);
}
#[test]
pub fn test_selector_array_wildcard() {
assert_eq!(
Selector::ArrayWildcard {}.eval(json_books()),
vec![
json_first_book(),
json_second_book(),
json_third_book(),
json_fourth_book()
]
);
} }
#[test] #[test]
@ -342,7 +391,7 @@ mod tests {
key: "key".to_string(), key: "key".to_string(),
func: PredicateFunc::LessThan(Number { func: PredicateFunc::LessThan(Number {
int: 10, int: 10,
decimal: 0 decimal: 0,
}), }),
} }
.eval(json!({"key": 1})), .eval(json!({"key": 1})),

View File

@ -48,6 +48,7 @@ fn selector(reader: &mut Reader) -> ParseResult<Selector> {
vec![ vec![
selector_recursive_key, selector_recursive_key,
selector_array_index, selector_array_index,
selector_array_wildcard,
selector_object_key_bracket, selector_object_key_bracket,
selector_object_key, selector_object_key,
selector_filter, selector_filter,
@ -72,6 +73,13 @@ fn selector_array_index(reader: &mut Reader) -> Result<Selector, Error> {
Ok(Selector::ArrayIndex(i)) Ok(Selector::ArrayIndex(i))
} }
fn selector_array_wildcard(reader: &mut Reader) -> Result<Selector, Error> {
try_left_bracket(reader)?;
try_literal("*", reader)?;
literal("]", reader)?;
Ok(Selector::ArrayWildcard {})
}
fn selector_filter(reader: &mut Reader) -> Result<Selector, Error> { fn selector_filter(reader: &mut Reader) -> Result<Selector, Error> {
try_literal("[?(", reader)?; try_literal("[?(", reader)?;
let pred = predicate(reader)?; let pred = predicate(reader)?;
@ -328,6 +336,19 @@ mod tests {
assert_eq!(reader.state.cursor, 4); assert_eq!(reader.state.cursor, 4);
} }
#[test]
pub fn test_selector_wildcard() {
let mut reader = Reader::init("[*]");
assert_eq!(selector(&mut reader).unwrap(), Selector::ArrayWildcard {});
assert_eq!(reader.state.cursor, 3);
// you don't need to keep the exact string
// this is not part of the AST
let mut reader = Reader::init(".[*]");
assert_eq!(selector(&mut reader).unwrap(), Selector::ArrayWildcard {});
assert_eq!(reader.state.cursor, 4);
}
#[test] #[test]
pub fn test_key_bracket_selector() { pub fn test_key_bracket_selector() {
let mut reader = Reader::init("['key']"); let mut reader = Reader::init("['key']");

View File

@ -38,6 +38,17 @@ fn test_bookstore_path() {
let value: serde_json::Value = let value: serde_json::Value =
serde_json::from_str(s.as_str()).expect("could not parse json file"); serde_json::from_str(s.as_str()).expect("could not parse json file");
// $.store.book[*].author
assert_eq!(
test_ok("$.store.book[*].author", value.clone()),
vec![
json!("Nigel Rees"),
json!("Evelyn Waugh"),
json!("Herman Melville"),
json!("J. R. R. Tolkien")
]
);
assert_eq!( assert_eq!(
test_ok("$.store.book[0].title", value.clone()), test_ok("$.store.book[0].title", value.clone()),
vec![json!("Sayings of the Century")] vec![json!("Sayings of the Century")]