Resolved #6 (from v0.2.2), new array filter (range step, escaped quote notation, array keys)

This commit is contained in:
freestrings 2019-06-19 11:38:12 +09:00
commit ebd49c2205
17 changed files with 510 additions and 113 deletions

View File

@ -1,6 +1,6 @@
[package] [package]
name = "jsonpath_lib" name = "jsonpath_lib"
version = "0.2.0" version = "0.2.2"
authors = ["Changseok Han <freestrings@gmail.com>"] authors = ["Changseok Han <freestrings@gmail.com>"]
description = "It is JsonPath engine written in Rust. it provide a similar API interface in Webassembly and Javascript also. - Webassembly Demo: https://freestrings.github.io/jsonpath" description = "It is JsonPath engine written in Rust. it provide a similar API interface in Webassembly and Javascript also. - Webassembly Demo: https://freestrings.github.io/jsonpath"

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -58,23 +58,26 @@
/******/ "__wbindgen_cb_forget": function(p0i32) { /******/ "__wbindgen_cb_forget": function(p0i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_cb_forget"](p0i32); /******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_cb_forget"](p0i32);
/******/ }, /******/ },
/******/ "__wbg_error_af8a3e3880eae1c8": function(p0i32,p1i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbg_error_af8a3e3880eae1c8"](p0i32,p1i32);
/******/ },
/******/ "__wbindgen_object_drop_ref": function(p0i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_object_drop_ref"](p0i32);
/******/ },
/******/ "__wbindgen_string_new": function(p0i32,p1i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_string_new"](p0i32,p1i32);
/******/ },
/******/ "__wbindgen_json_parse": function(p0i32,p1i32) { /******/ "__wbindgen_json_parse": function(p0i32,p1i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_json_parse"](p0i32,p1i32); /******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_json_parse"](p0i32,p1i32);
/******/ }, /******/ },
/******/ "__wbindgen_json_serialize": function(p0i32,p1i32) { /******/ "__wbindgen_json_serialize": function(p0i32,p1i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_json_serialize"](p0i32,p1i32); /******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_json_serialize"](p0i32,p1i32);
/******/ }, /******/ },
/******/ "__wbg_call_88d2a6153573084e": function(p0i32,p1i32,p2i32,p3i32) { /******/ "__wbg_error_8015049cb5adfca2": function(p0i32,p1i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbg_call_88d2a6153573084e"](p0i32,p1i32,p2i32,p3i32); /******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbg_error_8015049cb5adfca2"](p0i32,p1i32);
/******/ },
/******/ "__wbindgen_object_drop_ref": function(p0i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_object_drop_ref"](p0i32);
/******/ },
/******/ "__wbindgen_object_clone_ref": function(p0i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_object_clone_ref"](p0i32);
/******/ },
/******/ "__wbindgen_string_new": function(p0i32,p1i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_string_new"](p0i32,p1i32);
/******/ },
/******/ "__wbg_call_972de3aa550c37b2": function(p0i32,p1i32,p2i32,p3i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbg_call_972de3aa550c37b2"](p0i32,p1i32,p2i32,p3i32);
/******/ }, /******/ },
/******/ "__wbindgen_is_string": function(p0i32) { /******/ "__wbindgen_is_string": function(p0i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_is_string"](p0i32); /******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_is_string"](p0i32);
@ -82,9 +85,6 @@
/******/ "__wbindgen_string_get": function(p0i32,p1i32) { /******/ "__wbindgen_string_get": function(p0i32,p1i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_string_get"](p0i32,p1i32); /******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_string_get"](p0i32,p1i32);
/******/ }, /******/ },
/******/ "__wbindgen_object_clone_ref": function(p0i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_object_clone_ref"](p0i32);
/******/ },
/******/ "__wbindgen_debug_string": function(p0i32,p1i32) { /******/ "__wbindgen_debug_string": function(p0i32,p1i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_debug_string"](p0i32,p1i32); /******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_debug_string"](p0i32,p1i32);
/******/ }, /******/ },
@ -94,11 +94,11 @@
/******/ "__wbindgen_rethrow": function(p0i32) { /******/ "__wbindgen_rethrow": function(p0i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_rethrow"](p0i32); /******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_rethrow"](p0i32);
/******/ }, /******/ },
/******/ "__wbindgen_closure_wrapper22": function(p0i32,p1i32,p2i32) { /******/ "__wbindgen_closure_wrapper18": function(p0i32,p1i32,p2i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_closure_wrapper22"](p0i32,p1i32,p2i32); /******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_closure_wrapper18"](p0i32,p1i32,p2i32);
/******/ }, /******/ },
/******/ "__wbindgen_closure_wrapper24": function(p0i32,p1i32,p2i32) { /******/ "__wbindgen_closure_wrapper20": function(p0i32,p1i32,p2i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_closure_wrapper24"](p0i32,p1i32,p2i32); /******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_closure_wrapper20"](p0i32,p1i32,p2i32);
/******/ } /******/ }
/******/ } /******/ }
/******/ }; /******/ };
@ -198,7 +198,7 @@
/******/ promises.push(installedWasmModuleData); /******/ promises.push(installedWasmModuleData);
/******/ else { /******/ else {
/******/ var importObject = wasmImportObjects[wasmModuleId](); /******/ var importObject = wasmImportObjects[wasmModuleId]();
/******/ var req = fetch(__webpack_require__.p + "" + {"../all_pkg/jsonpath_wasm_bg.wasm":"7a2fe8020c3403dd4ce6"}[wasmModuleId] + ".module.wasm"); /******/ var req = fetch(__webpack_require__.p + "" + {"../all_pkg/jsonpath_wasm_bg.wasm":"936e94ea88fa30f5750a"}[wasmModuleId] + ".module.wasm");
/******/ var promise; /******/ var promise;
/******/ if(importObject instanceof Promise && typeof WebAssembly.compileStreaming === 'function') { /******/ if(importObject instanceof Promise && typeof WebAssembly.compileStreaming === 'function') {
/******/ promise = Promise.all([WebAssembly.compileStreaming(req), importObject]).then(function(items) { /******/ promise = Promise.all([WebAssembly.compileStreaming(req), importObject]).then(function(items) {

38
docs/bootstrap.js vendored
View File

@ -58,23 +58,26 @@
/******/ "__wbindgen_cb_forget": function(p0i32) { /******/ "__wbindgen_cb_forget": function(p0i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_cb_forget"](p0i32); /******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_cb_forget"](p0i32);
/******/ }, /******/ },
/******/ "__wbg_error_af8a3e3880eae1c8": function(p0i32,p1i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbg_error_af8a3e3880eae1c8"](p0i32,p1i32);
/******/ },
/******/ "__wbindgen_object_drop_ref": function(p0i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_object_drop_ref"](p0i32);
/******/ },
/******/ "__wbindgen_string_new": function(p0i32,p1i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_string_new"](p0i32,p1i32);
/******/ },
/******/ "__wbindgen_json_parse": function(p0i32,p1i32) { /******/ "__wbindgen_json_parse": function(p0i32,p1i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_json_parse"](p0i32,p1i32); /******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_json_parse"](p0i32,p1i32);
/******/ }, /******/ },
/******/ "__wbindgen_json_serialize": function(p0i32,p1i32) { /******/ "__wbindgen_json_serialize": function(p0i32,p1i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_json_serialize"](p0i32,p1i32); /******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_json_serialize"](p0i32,p1i32);
/******/ }, /******/ },
/******/ "__wbg_call_88d2a6153573084e": function(p0i32,p1i32,p2i32,p3i32) { /******/ "__wbg_error_8015049cb5adfca2": function(p0i32,p1i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbg_call_88d2a6153573084e"](p0i32,p1i32,p2i32,p3i32); /******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbg_error_8015049cb5adfca2"](p0i32,p1i32);
/******/ },
/******/ "__wbindgen_object_drop_ref": function(p0i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_object_drop_ref"](p0i32);
/******/ },
/******/ "__wbindgen_object_clone_ref": function(p0i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_object_clone_ref"](p0i32);
/******/ },
/******/ "__wbindgen_string_new": function(p0i32,p1i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_string_new"](p0i32,p1i32);
/******/ },
/******/ "__wbg_call_972de3aa550c37b2": function(p0i32,p1i32,p2i32,p3i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbg_call_972de3aa550c37b2"](p0i32,p1i32,p2i32,p3i32);
/******/ }, /******/ },
/******/ "__wbindgen_is_string": function(p0i32) { /******/ "__wbindgen_is_string": function(p0i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_is_string"](p0i32); /******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_is_string"](p0i32);
@ -82,9 +85,6 @@
/******/ "__wbindgen_string_get": function(p0i32,p1i32) { /******/ "__wbindgen_string_get": function(p0i32,p1i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_string_get"](p0i32,p1i32); /******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_string_get"](p0i32,p1i32);
/******/ }, /******/ },
/******/ "__wbindgen_object_clone_ref": function(p0i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_object_clone_ref"](p0i32);
/******/ },
/******/ "__wbindgen_debug_string": function(p0i32,p1i32) { /******/ "__wbindgen_debug_string": function(p0i32,p1i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_debug_string"](p0i32,p1i32); /******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_debug_string"](p0i32,p1i32);
/******/ }, /******/ },
@ -94,11 +94,11 @@
/******/ "__wbindgen_rethrow": function(p0i32) { /******/ "__wbindgen_rethrow": function(p0i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_rethrow"](p0i32); /******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_rethrow"](p0i32);
/******/ }, /******/ },
/******/ "__wbindgen_closure_wrapper22": function(p0i32,p1i32,p2i32) { /******/ "__wbindgen_closure_wrapper18": function(p0i32,p1i32,p2i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_closure_wrapper22"](p0i32,p1i32,p2i32); /******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_closure_wrapper18"](p0i32,p1i32,p2i32);
/******/ }, /******/ },
/******/ "__wbindgen_closure_wrapper24": function(p0i32,p1i32,p2i32) { /******/ "__wbindgen_closure_wrapper20": function(p0i32,p1i32,p2i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_closure_wrapper24"](p0i32,p1i32,p2i32); /******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_closure_wrapper20"](p0i32,p1i32,p2i32);
/******/ } /******/ }
/******/ } /******/ }
/******/ }; /******/ };
@ -198,7 +198,7 @@
/******/ promises.push(installedWasmModuleData); /******/ promises.push(installedWasmModuleData);
/******/ else { /******/ else {
/******/ var importObject = wasmImportObjects[wasmModuleId](); /******/ var importObject = wasmImportObjects[wasmModuleId]();
/******/ var req = fetch(__webpack_require__.p + "" + {"../all_pkg/jsonpath_wasm_bg.wasm":"7a2fe8020c3403dd4ce6"}[wasmModuleId] + ".module.wasm"); /******/ var req = fetch(__webpack_require__.p + "" + {"../all_pkg/jsonpath_wasm_bg.wasm":"936e94ea88fa30f5750a"}[wasmModuleId] + ".module.wasm");
/******/ var promise; /******/ var promise;
/******/ if(importObject instanceof Promise && typeof WebAssembly.compileStreaming === 'function') { /******/ if(importObject instanceof Promise && typeof WebAssembly.compileStreaming === 'function') {
/******/ promise = Promise.all([WebAssembly.compileStreaming(req), importObject]).then(function(items) { /******/ promise = Promise.all([WebAssembly.compileStreaming(req), importObject]).then(function(items) {

View File

@ -398,6 +398,93 @@ describe('filter test', () => {
run (done, i, list[i]); run (done, i, list[i]);
}) })
} }
it('object equal', (done) => {
let selector = new jsonpath.Selector();
selector.path('$..[?(@.a == 1)]');
selector.value({
'a': 1,
'b': {'a': 1},
'c': {'a': 1},
});
let result = selector.select();
if (JSON.stringify(result) === JSON.stringify([{'a': 1}, {'a': 1}])) {
done();
}
});
// it('escaped single quote notation', (done) => {
// let result = jsonpath.select({"single'quote":"value"}, "$['single\\'quote']");
// if (JSON.stringify(result) === JSON.stringify(["value"])) {
// done();
// }
// });
//
// it('escaped double quote notation', (done) => {
// let result = jsonpath.select({"single\"quote":"value"}, "$['single\"quote']");
// if (JSON.stringify(result) === JSON.stringify(["value"])) {
// done();
// }
// });
//
// it('array range with step - $[::]', (done) => {
// let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[::]");
// if (JSON.stringify(result) === JSON.stringify(["first", "second", "third", "forth", "fifth"])) {
// done();
// }
// });
//
// it('array range with step - $[::2]', (done) => {
// let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[::2]");
// if (JSON.stringify(result) === JSON.stringify(["first", "third", "fifth"])) {
// done();
// }
// });
//
// it('array range with step - $[1: :]', (done) => {
// let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[1: :]");
// if (JSON.stringify(result) === JSON.stringify(["second", "third", "forth", "fifth"])) {
// done();
// }
// });
//
// it('array range with step - $[1:2:]', (done) => {
// let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[1:2:]");
// if (JSON.stringify(result) === JSON.stringify(["second"])) {
// done();
// }
// });
//
// it('array range with step - $[1::2]', (done) => {
// let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[1::2]");
// if (JSON.stringify(result) === JSON.stringify(["second", "forth"])) {
// done();
// }
// });
//
// it('array range with step - $[0:3:1]', (done) => {
// let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[0:3:1]");
// if (JSON.stringify(result) === JSON.stringify(["first", "second", "third"])) {
// done();
// }
// });
//
// it('array range with step - $[0:3:2]', (done) => {
// let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[0:3:2]");
// if (JSON.stringify(result) === JSON.stringify(["first", "third"])) {
// done();
// }
// });
//
// it('array keys', (done) => {
// let result = jsonpath.select({
// "key1": "value1",
// "key2": 2
// }, "$['key1', 'key2']");
// if (JSON.stringify(result) === JSON.stringify(["value1", 2])) {
// done();
// }
// });
}); });
describe('SelectorMut test', () => { describe('SelectorMut test', () => {
@ -797,4 +884,13 @@ describe('README test', () => {
done(); done();
} }
}); });
}); });
// describe('ISSUE test', () => {
// it('Results do not match other implementations #6', (done) => {
// let result = jsonpath.select(["first", "second"], "$[:]");
// if (JSON.stringify(result) === JSON.stringify(["first", "second"])) {
// done();
// }
// });
// });

View File

@ -4,7 +4,7 @@ pub mod parser;
#[cfg(test)] #[cfg(test)]
mod parser_tests { mod parser_tests {
use parser::parser::{ParseToken, Parser, NodeVisitor, FilterToken}; use parser::parser::{FilterToken, NodeVisitor, Parser, ParseToken};
struct NodeVisitorTestImpl<'a> { struct NodeVisitorTestImpl<'a> {
input: &'a str, input: &'a str,
@ -162,21 +162,63 @@ mod parser_tests {
assert_eq!(run("$.a[10:]"), Ok(vec![ assert_eq!(run("$.a[10:]"), Ok(vec![
ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()), ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()),
ParseToken::Array, ParseToken::Array,
ParseToken::Range(Some(10), None), ParseToken::Range(Some(10), None, None),
ParseToken::ArrayEof ParseToken::ArrayEof
])); ]));
assert_eq!(run("$.a[:11]"), Ok(vec![ assert_eq!(run("$.a[:11]"), Ok(vec![
ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()), ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()),
ParseToken::Array, ParseToken::Array,
ParseToken::Range(None, Some(11)), ParseToken::Range(None, Some(11), None),
ParseToken::ArrayEof ParseToken::ArrayEof
])); ]));
assert_eq!(run("$.a[-12:13]"), Ok(vec![ assert_eq!(run("$.a[-12:13]"), Ok(vec![
ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()), ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()),
ParseToken::Array, ParseToken::Array,
ParseToken::Range(Some(-12), Some(13)), ParseToken::Range(Some(-12), Some(13), None),
ParseToken::ArrayEof
]));
assert_eq!(run(r#"$[0:3:2]"#), Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Range(Some(0), Some(3), Some(2)),
ParseToken::ArrayEof
]));
assert_eq!(run(r#"$[:3:2]"#), Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Range(None, Some(3), Some(2)),
ParseToken::ArrayEof
]));
assert_eq!(run(r#"$[:]"#), Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Range(None, None, None),
ParseToken::ArrayEof
]));
assert_eq!(run(r#"$[::]"#), Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Range(None, None, None),
ParseToken::ArrayEof
]));
assert_eq!(run(r#"$[::2]"#), Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Range(None, None, Some(2)),
ParseToken::ArrayEof
]));
assert_eq!(run(r#"$["a", 'b']"#), Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Keys(vec!["a".to_string(), "b".to_string()]),
ParseToken::ArrayEof ParseToken::ArrayEof
])); ]));
@ -263,6 +305,27 @@ mod parser_tests {
ParseToken::ArrayEof ParseToken::ArrayEof
])); ]));
assert_eq!(run("$[:]"), Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Range(None, None, None),
ParseToken::ArrayEof
]));
assert_eq!(run(r#"$['single\'quote']"#), Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Key("single'quote".to_string()),
ParseToken::ArrayEof
]));
assert_eq!(run(r#"$["single\"quote"]"#), Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Key(r#"single"quote"#.to_string()),
ParseToken::ArrayEof
]));
match run("$[") { match run("$[") {
Ok(_) => panic!(), Ok(_) => panic!(),
_ => {} _ => {}
@ -331,6 +394,10 @@ mod parser_tests {
mod tokenizer_tests { mod tokenizer_tests {
use parser::tokenizer::{Token, TokenError, Tokenizer, TokenReader}; use parser::tokenizer::{Token, TokenError, Tokenizer, TokenReader};
fn setup() {
let _ = env_logger::try_init();
}
fn collect_token(input: &str) -> (Vec<Token>, Option<TokenError>) { fn collect_token(input: &str) -> (Vec<Token>, Option<TokenError>) {
let mut tokenizer = Tokenizer::new(input); let mut tokenizer = Tokenizer::new(input);
let mut vec = vec![]; let mut vec = vec![];
@ -373,6 +440,8 @@ mod tokenizer_tests {
#[test] #[test]
fn token() { fn token() {
setup();
run("$.01.a", run("$.01.a",
( (
vec![ vec![
@ -520,5 +589,26 @@ mod tokenizer_tests {
] ]
, Some(TokenError::Eof) , Some(TokenError::Eof)
)); ));
run("$[:]", (vec![
Token::Absolute(0),
Token::OpenArray(1),
Token::Split(2),
Token::CloseArray(3)
], Some(TokenError::Eof)));
run(r#"$['single\'quote']"#, (vec![
Token::Absolute(0),
Token::OpenArray(1),
Token::SingleQuoted(2, "single\'quote".to_string()),
Token::CloseArray(17)
], Some(TokenError::Eof)));
run(r#"$["double\"quote"]"#, (vec![
Token::Absolute(0),
Token::OpenArray(1),
Token::DoubleQuoted(2, "double\"quote".to_string()),
Token::CloseArray(17)
], Some(TokenError::Eof)));
} }
} }

View File

@ -1,3 +1,5 @@
use std::str::FromStr;
use super::tokenizer::*; use super::tokenizer::*;
const DUMMY: usize = 0; const DUMMY: usize = 0;
@ -5,17 +7,12 @@ const DUMMY: usize = 0;
type ParseResult<T> = Result<T, String>; type ParseResult<T> = Result<T, String>;
mod utils { mod utils {
pub fn string_to_isize<F>(string: &String, msg_handler: F) -> Result<isize, String> use std::str::FromStr;
where F: Fn() -> String {
match string.as_str().parse::<isize>() {
Ok(n) => Ok(n),
_ => Err(msg_handler())
}
}
pub fn string_to_f64<F>(string: &String, msg_handler: F) -> Result<f64, String> pub fn string_to_num<F, S: FromStr>(string: &String, msg_handler: F) -> Result<S, String>
where F: Fn() -> String { where F: Fn() -> String
match string.as_str().parse::<f64>() { {
match string.as_str().parse() {
Ok(n) => Ok(n), Ok(n) => Ok(n),
_ => Err(msg_handler()) _ => Err(msg_handler())
} }
@ -36,6 +33,7 @@ pub enum ParseToken {
All, All,
Key(String), Key(String),
Keys(Vec<String>),
// [] // []
Array, Array,
// 메타토큰 // 메타토큰
@ -43,7 +41,7 @@ pub enum ParseToken {
// ?( filter ) // ?( filter )
Filter(FilterToken), Filter(FilterToken),
// 1 : 2 // 1 : 2
Range(Option<isize>, Option<isize>), Range(Option<isize>, Option<isize>, Option<usize>),
// 1, 2, 3 // 1, 2, 3
Union(Vec<isize>), Union(Vec<isize>),
@ -235,12 +233,39 @@ impl Parser {
} }
} }
fn array_quota_value(tokenizer: &mut TokenReader) -> ParseResult<Node> { fn array_keys(tokenizer: &mut TokenReader, first_key: String) -> ParseResult<Node> {
debug!("#array_quota_value"); let mut keys = vec![first_key];
while tokenizer.peek_is(COMMA) {
Self::eat_token(tokenizer);
Self::eat_whitespace(tokenizer);
if !(tokenizer.peek_is(SINGLE_QUOTE) || tokenizer.peek_is(DOUBLE_QUOTE)) {
return Err(tokenizer.err_msg());
}
match tokenizer.next_token() {
Ok(Token::SingleQuoted(_, val))
| Ok(Token::DoubleQuoted(_, val)) => {
keys.push(val);
}
_ => {}
}
Self::eat_whitespace(tokenizer);
}
Ok(Self::node(ParseToken::Keys(keys)))
}
fn array_quote_value(tokenizer: &mut TokenReader) -> ParseResult<Node> {
debug!("#array_quote_value");
match tokenizer.next_token() { match tokenizer.next_token() {
Ok(Token::SingleQuoted(_, val)) Ok(Token::SingleQuoted(_, val)) | Ok(Token::DoubleQuoted(_, val)) => {
| Ok(Token::DoubleQuoted(_, val)) => { if !tokenizer.peek_is(COMMA) {
Ok(Self::node(ParseToken::Key(val))) Ok(Self::node(ParseToken::Key(val)))
} else {
Self::array_keys(tokenizer, val)
}
} }
Err(TokenError::Eof) => { Err(TokenError::Eof) => {
Ok(Self::node(ParseToken::Eof)) Ok(Self::node(ParseToken::Eof))
@ -291,7 +316,7 @@ impl Parser {
debug!("#array_value_key"); debug!("#array_value_key");
match tokenizer.next_token() { match tokenizer.next_token() {
Ok(Token::Key(pos, ref val)) => { Ok(Token::Key(pos, ref val)) => {
let digit = utils::string_to_isize(val, || tokenizer.err_msg_with_pos(pos))?; let digit = utils::string_to_num(val, || tokenizer.err_msg_with_pos(pos))?;
Self::eat_whitespace(tokenizer); Self::eat_whitespace(tokenizer);
match tokenizer.peek_token() { match tokenizer.peek_token() {
@ -325,7 +350,7 @@ impl Parser {
} }
Ok(Token::DoubleQuoted(_, _)) Ok(Token::DoubleQuoted(_, _))
| Ok(Token::SingleQuoted(_, _)) => { | Ok(Token::SingleQuoted(_, _)) => {
Self::array_quota_value(tokenizer) Self::array_quote_value(tokenizer)
} }
Err(TokenError::Eof) => { Err(TokenError::Eof) => {
Ok(Self::node(ParseToken::Eof)) Ok(Self::node(ParseToken::Eof))
@ -348,7 +373,7 @@ impl Parser {
Self::eat_whitespace(tokenizer); Self::eat_whitespace(tokenizer);
match tokenizer.next_token() { match tokenizer.next_token() {
Ok(Token::Key(pos, ref val)) => { Ok(Token::Key(pos, ref val)) => {
let digit = utils::string_to_isize(val, || tokenizer.err_msg_with_pos(pos))?; let digit = utils::string_to_num(val, || tokenizer.err_msg_with_pos(pos))?;
values.push(digit); values.push(digit);
} }
_ => { _ => {
@ -359,26 +384,70 @@ impl Parser {
Ok(Self::node(ParseToken::Union(values))) Ok(Self::node(ParseToken::Union(values)))
} }
fn range_from(num: isize, tokenizer: &mut TokenReader) -> ParseResult<Node> { fn range_value<S: FromStr>(tokenizer: &mut TokenReader) -> Result<Option<S>, String> {
if tokenizer.peek_is(SPLIT) {
Self::eat_token(tokenizer);
Self::eat_whitespace(tokenizer);
if tokenizer.peek_is(KEY) {
match tokenizer.next_token() {
Ok(Token::Key(pos, str_step)) => {
match utils::string_to_num(&str_step, || tokenizer.err_msg_with_pos(pos)) {
Ok(step) => Ok(Some(step)),
Err(e) => Err(e)
}
}
_ => Ok(None)
}
} else {
Ok(None)
}
} else {
Ok(None)
}
}
fn range_from(from: isize, tokenizer: &mut TokenReader) -> ParseResult<Node> {
debug!("#range_from"); debug!("#range_from");
Self::eat_token(tokenizer); Self::eat_token(tokenizer);
Self::eat_whitespace(tokenizer); Self::eat_whitespace(tokenizer);
match tokenizer.peek_token() { match tokenizer.peek_token() {
Ok(Token::Key(_, _)) => { Ok(Token::Key(_, _)) => {
Self::range(num, tokenizer) Self::range(from, tokenizer)
}
Ok(Token::Split(_)) => {
match Self::range_value(tokenizer)? {
Some(step) => Ok(Self::node(ParseToken::Range(Some(from), None, Some(step)))),
_ => Ok(Self::node(ParseToken::Range(Some(from), None, None)))
}
} }
_ => { _ => {
Ok(Self::node(ParseToken::Range(Some(num), None))) Ok(Self::node(ParseToken::Range(Some(from), None, None)))
} }
} }
} }
fn range_to(tokenizer: &mut TokenReader) -> ParseResult<Node> { fn range_to(tokenizer: &mut TokenReader) -> ParseResult<Node> {
debug!("#range_to"); debug!("#range_to");
match Self::range_value(tokenizer)? {
Some(step) => return Ok(Self::node(ParseToken::Range(None, None, Some(step)))),
_ => {}
}
match tokenizer.peek_token() {
Ok(Token::CloseArray(_)) => {
return Ok(Self::node(ParseToken::Range(None, None, None)));
}
_ => {}
}
match tokenizer.next_token() { match tokenizer.next_token() {
Ok(Token::Key(pos, ref val)) => { Ok(Token::Key(pos, ref to_str)) => {
let digit = utils::string_to_isize(val, || tokenizer.err_msg_with_pos(pos))?; let to = utils::string_to_num(to_str, || tokenizer.err_msg_with_pos(pos))?;
Ok(Self::node(ParseToken::Range(None, Some(digit)))) let step = Self::range_value(tokenizer)?;
Ok(Self::node(ParseToken::Range(None, Some(to), step)))
} }
_ => { _ => {
Err(tokenizer.err_msg()) Err(tokenizer.err_msg())
@ -386,12 +455,13 @@ impl Parser {
} }
} }
fn range(num: isize, tokenizer: &mut TokenReader) -> ParseResult<Node> { fn range(from: isize, tokenizer: &mut TokenReader) -> ParseResult<Node> {
debug!("#range"); debug!("#range");
match tokenizer.next_token() { match tokenizer.next_token() {
Ok(Token::Key(pos, ref val)) => { Ok(Token::Key(pos, ref str_to)) => {
let digit = utils::string_to_isize(val, || tokenizer.err_msg_with_pos(pos))?; let to = utils::string_to_num(str_to, || tokenizer.err_msg_with_pos(pos))?;
Ok(Self::node(ParseToken::Range(Some(num), Some(digit)))) let step = Self::range_value(tokenizer)?;
Ok(Self::node(ParseToken::Range(Some(from), Some(to), step)))
} }
_ => { _ => {
Err(tokenizer.err_msg()) Err(tokenizer.err_msg())
@ -498,7 +568,7 @@ impl Parser {
Self::term_num_float(val.as_str(), tokenizer) Self::term_num_float(val.as_str(), tokenizer)
} }
_ => { _ => {
let number = utils::string_to_f64(&val, || tokenizer.err_msg_with_pos(pos))?; let number = utils::string_to_num(&val, || tokenizer.err_msg_with_pos(pos))?;
Ok(Self::node(ParseToken::Number(number))) Ok(Self::node(ParseToken::Number(number)))
} }
} }
@ -521,7 +591,7 @@ impl Parser {
f.push_str(&mut num); f.push_str(&mut num);
f.push('.'); f.push('.');
f.push_str(frac.as_str()); f.push_str(frac.as_str());
let number = utils::string_to_f64(&f, || tokenizer.err_msg_with_pos(pos))?; let number = utils::string_to_num(&f, || tokenizer.err_msg_with_pos(pos))?;
Ok(Self::node(ParseToken::Number(number))) Ok(Self::node(ParseToken::Number(number)))
} }
_ => { _ => {
@ -560,8 +630,8 @@ impl Parser {
return Self::json_path(tokenizer); return Self::json_path(tokenizer);
} }
if tokenizer.peek_is(DOUBLE_QUOTA) || tokenizer.peek_is(SINGLE_QUOTA) { if tokenizer.peek_is(DOUBLE_QUOTE) || tokenizer.peek_is(SINGLE_QUOTE) {
return Self::array_quota_value(tokenizer); return Self::array_quote_value(tokenizer);
} }
if tokenizer.peek_is(KEY) { if tokenizer.peek_is(KEY) {
@ -652,7 +722,8 @@ pub trait NodeVisitor {
| ParseToken::Relative | ParseToken::Relative
| ParseToken::All | ParseToken::All
| ParseToken::Key(_) | ParseToken::Key(_)
| ParseToken::Range(_, _) | ParseToken::Keys(_)
| ParseToken::Range(_, _, _)
| ParseToken::Union(_) | ParseToken::Union(_)
| ParseToken::Number(_) | ParseToken::Number(_)
| ParseToken::Bool(_) => { | ParseToken::Bool(_) => {

View File

@ -15,8 +15,8 @@ pub const SPLIT: &'static str = ":";
pub const OPEN_PARENTHESIS: &'static str = "("; pub const OPEN_PARENTHESIS: &'static str = "(";
pub const CLOSE_PARENTHESIS: &'static str = ")"; pub const CLOSE_PARENTHESIS: &'static str = ")";
pub const KEY: &'static str = "Key"; pub const KEY: &'static str = "Key";
pub const DOUBLE_QUOTA: &'static str = "\""; pub const DOUBLE_QUOTE: &'static str = "\"";
pub const SINGLE_QUOTA: &'static str = "'"; pub const SINGLE_QUOTE: &'static str = "'";
pub const EQUAL: &'static str = "=="; pub const EQUAL: &'static str = "==";
pub const GREATER_OR_EQUAL: &'static str = ">="; pub const GREATER_OR_EQUAL: &'static str = ">=";
pub const GREATER: &'static str = ">"; pub const GREATER: &'static str = ">";
@ -44,8 +44,8 @@ const CH_PIPE: char = '|';
const CH_LITTLE: char = '<'; const CH_LITTLE: char = '<';
const CH_GREATER: char = '>'; const CH_GREATER: char = '>';
const CH_EXCLAMATION: char = '!'; const CH_EXCLAMATION: char = '!';
const CH_SINGLE_QUOTA: char = '\''; const CH_SINGLE_QUOTE: char = '\'';
const CH_DOUBLE_QUOTA: char = '"'; const CH_DOUBLE_QUOTE: char = '"';
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum TokenError { pub enum TokenError {
@ -109,8 +109,8 @@ impl Token {
Token::OpenParenthesis(_) => OPEN_PARENTHESIS, Token::OpenParenthesis(_) => OPEN_PARENTHESIS,
Token::CloseParenthesis(_) => CLOSE_PARENTHESIS, Token::CloseParenthesis(_) => CLOSE_PARENTHESIS,
Token::Key(_, _) => KEY, Token::Key(_, _) => KEY,
Token::DoubleQuoted(_, _) => DOUBLE_QUOTA, Token::DoubleQuoted(_, _) => DOUBLE_QUOTE,
Token::SingleQuoted(_, _) => SINGLE_QUOTA, Token::SingleQuoted(_, _) => SINGLE_QUOTE,
Token::Equal(_) => EQUAL, Token::Equal(_) => EQUAL,
Token::GreaterOrEqual(_) => GREATER_OR_EQUAL, Token::GreaterOrEqual(_) => GREATER_OR_EQUAL,
Token::Greater(_) => GREATER, Token::Greater(_) => GREATER,
@ -147,20 +147,36 @@ pub struct Tokenizer<'a> {
impl<'a> Tokenizer<'a> { impl<'a> Tokenizer<'a> {
pub fn new(input: &'a str) -> Self { pub fn new(input: &'a str) -> Self {
trace!("input: {}", input);
Tokenizer { Tokenizer {
input: PathReader::new(input), input: PathReader::new(input),
} }
} }
fn single_quota(&mut self, pos: usize, ch: char) -> Result<Token, TokenError> { fn quote(&mut self, ch: char) -> Result<String, TokenError> {
let (_, val) = self.input.take_while(|c| *c != ch).map_err(to_token_error)?; let (_, mut val) = self.input.take_while(|c| *c != ch).map_err(to_token_error)?;
self.input.next_char().map_err(to_token_error)?;
if let Some('\\') = val.chars().last() {
self.input.next_char().map_err(to_token_error)?;
let _ = val.pop();
let (_, mut val_remain) = self.input.take_while(|c| *c != ch).map_err(to_token_error)?;
self.input.next_char().map_err(to_token_error)?;
val.push(ch);
val.push_str(val_remain.as_str());
} else {
self.input.next_char().map_err(to_token_error)?;
}
Ok(val)
}
fn single_quote(&mut self, pos: usize, ch: char) -> Result<Token, TokenError> {
let val = self.quote(ch)?;
Ok(Token::SingleQuoted(pos, val)) Ok(Token::SingleQuoted(pos, val))
} }
fn double_quota(&mut self, pos: usize, ch: char) -> Result<Token, TokenError> { fn double_quote(&mut self, pos: usize, ch: char) -> Result<Token, TokenError> {
let (_, val) = self.input.take_while(|c| *c != ch).map_err(to_token_error)?; let val = self.quote(ch)?;
self.input.next_char().map_err(to_token_error)?;
Ok(Token::DoubleQuoted(pos, val)) Ok(Token::DoubleQuoted(pos, val))
} }
@ -259,8 +275,8 @@ impl<'a> Tokenizer<'a> {
Some(t) => Ok(t), Some(t) => Ok(t),
None => { None => {
match ch { match ch {
CH_SINGLE_QUOTA => self.single_quota(pos, ch), CH_SINGLE_QUOTE => self.single_quote(pos, ch),
CH_DOUBLE_QUOTA => self.double_quota(pos, ch), CH_DOUBLE_QUOTE => self.double_quote(pos, ch),
CH_EQUAL => self.equal(pos, ch), CH_EQUAL => self.equal(pos, ch),
CH_GREATER => self.greater(pos, ch), CH_GREATER => self.greater(pos, ch),
CH_LITTLE => self.little(pos, ch), CH_LITTLE => self.little(pos, ch),

View File

@ -693,20 +693,22 @@ impl<'a, 'b> Selector<'a, 'b> {
debug!("next_from_current_with_num : {:?}, {:?}", &index, self.current); debug!("next_from_current_with_num : {:?}, {:?}", &index, self.current);
} }
fn next_from_current_with_str(&mut self, key: &str) { fn next_from_current_with_str(&mut self, keys: &Vec<String>) {
fn _collect<'a>(v: &'a Value, tmp: &mut Vec<&'a Value>, key: &str, visited: &mut HashSet<*const Value>) { fn _collect<'a>(v: &'a Value, tmp: &mut Vec<&'a Value>, keys: &Vec<String>, visited: &mut HashSet<*const Value>) {
match v { match v {
Value::Object(map) => { Value::Object(map) => {
if let Some(v) = map.get(key) { for key in keys {
let ptr = v as *const Value; if let Some(v) = map.get(key) {
if !visited.contains(&ptr) { let ptr = v as *const Value;
visited.insert(ptr); if !visited.contains(&ptr) {
tmp.push(v) visited.insert(ptr);
tmp.push(v)
}
} }
} }
} }
Value::Array(vec) => for v in vec { Value::Array(vec) => for v in vec {
_collect(v, tmp, key, visited); _collect(v, tmp, keys, visited);
} }
_ => {} _ => {}
} }
@ -716,12 +718,12 @@ impl<'a, 'b> Selector<'a, 'b> {
let mut tmp = Vec::new(); let mut tmp = Vec::new();
let mut visited = HashSet::new(); let mut visited = HashSet::new();
for c in current { for c in current {
_collect(c, &mut tmp, key, &mut visited); _collect(c, &mut tmp, keys, &mut visited);
} }
self.current = Some(tmp); self.current = Some(tmp);
} }
debug!("next_from_current_with_str : {}, {:?}", key, self.current); debug!("next_from_current_with_str : {:?}, {:?}", keys, self.current);
} }
fn next_all_from_current(&mut self) { fn next_all_from_current(&mut self) {
@ -838,7 +840,7 @@ impl<'a, 'b> NodeVisitor for Selector<'a, 'b> {
self.next_from_current_with_num(to_f64(&n)); self.next_from_current_with_num(to_f64(&n));
} }
ExprTerm::String(key) => { ExprTerm::String(key) => {
self.next_from_current_with_str(&key); self.next_from_current_with_str(&vec![key]);
} }
ExprTerm::Json(_, v) => { ExprTerm::Json(_, v) => {
if v.is_empty() { if v.is_empty() {
@ -886,7 +888,7 @@ impl<'a, 'b> NodeVisitor for Selector<'a, 'b> {
self.all_from_current_with_str(key.as_str()) self.all_from_current_with_str(key.as_str())
} }
ParseToken::In => { ParseToken::In => {
self.next_from_current_with_str(key.as_str()) self.next_from_current_with_str(&vec![key.clone()])
} }
_ => {} _ => {}
} }
@ -905,6 +907,17 @@ impl<'a, 'b> NodeVisitor for Selector<'a, 'b> {
_ => {} _ => {}
} }
} }
ParseToken::Keys(keys) => {
if !self.terms.is_empty() {
unimplemented!("keys in filter");
}
if let Some(ParseToken::Array) = self.tokens.pop() {
self.next_from_current_with_str(keys);
} else {
unreachable!();
}
}
ParseToken::Number(v) => { ParseToken::Number(v) => {
self.terms.push(Some(ExprTerm::Number(Number::from_f64(*v).unwrap()))); self.terms.push(Some(ExprTerm::Number(Number::from_f64(*v).unwrap())));
} }
@ -933,7 +946,7 @@ impl<'a, 'b> NodeVisitor for Selector<'a, 'b> {
unreachable!() unreachable!()
} }
} }
ParseToken::Range(from, to) => { ParseToken::Range(from, to, step) => {
if !self.terms.is_empty() { if !self.terms.is_empty() {
unimplemented!("range syntax in filter"); unimplemented!("range syntax in filter");
} }
@ -955,7 +968,10 @@ impl<'a, 'b> NodeVisitor for Selector<'a, 'b> {
vec.len() vec.len()
}; };
for i in from..to { for i in (from..to).step_by(match step {
Some(step) => *step,
_ => 1
}) {
if let Some(v) = vec.get(i) { if let Some(v) = vec.get(i) {
tmp.push(v); tmp.push(v);
} }

View File

@ -40,6 +40,10 @@ fn array() {
select_and_then_compare("$['school']['friends'][0].['name']", read_json("./benches/data_obj.json"), json!([ select_and_then_compare("$['school']['friends'][0].['name']", read_json("./benches/data_obj.json"), json!([
"Millicent Norman" "Millicent Norman"
])); ]));
select_and_then_compare(r#"$.["eyeColor", "name"]"#, read_json("./benches/data_obj.json"), json!([
"blue", "Leonor Herman"
]));
} }
#[test] #[test]
@ -349,4 +353,26 @@ fn filer_same_obj() {
{"a": 1}, {"a": 1},
{"a": 1} {"a": 1}
])); ]));
}
#[test]
fn range() {
setup();
select_and_then_compare("$[:]", json!(["first", "second"]), json!(["first", "second"]));
select_and_then_compare("$[::]", json!(["first", "second", "third", "forth", "fifth"]), json!(["first", "second", "third", "forth", "fifth"]));
select_and_then_compare("$[::2]", json!(["first", "second", "third", "forth", "fifth"]), json!(["first", "third", "fifth"]));
select_and_then_compare("$[1: :]", json!(["first", "second", "third", "forth", "fifth"]), json!(["second", "third", "forth", "fifth"]));
select_and_then_compare("$[1:2:]", json!(["first", "second", "third", "forth", "fifth"]), json!(["second"]));
select_and_then_compare("$[1::2]", json!(["first", "second", "third", "forth", "fifth"]), json!(["second", "forth"]));
select_and_then_compare("$[0:3:1]", json!(["first", "second", "third", "forth", "fifth"]), json!(["first", "second", "third"]));
select_and_then_compare("$[0:3:2]", json!(["first", "second", "third", "forth", "fifth"]), json!(["first", "third"]));
}
#[test]
fn quote() {
setup();
select_and_then_compare(r#"$['single\'quote']"#, json!({"single'quote":"value"}), json!(["value"]));
select_and_then_compare(r#"$["double\"quote"]"#, json!({"double\"quote":"value"}), json!(["value"]));
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "jsonpath-wasm" name = "jsonpath-wasm"
version = "0.2.1" version = "0.2.2"
authors = ["Changseok Han <freestrings@gmail.com>"] authors = ["Changseok Han <freestrings@gmail.com>"]
description = "It is Webassembly version of jsonpath_lib that is JsonPath engine written in Rust - Demo: https://freestrings.github.io/jsonpath" description = "It is Webassembly version of jsonpath_lib that is JsonPath engine written in Rust - Demo: https://freestrings.github.io/jsonpath"
keywords = ["jsonpath", "json", "webassembly", "parsing", "rust"] keywords = ["jsonpath", "json", "webassembly", "parsing", "rust"]

View File

@ -412,6 +412,79 @@ describe('filter test', () => {
done(); done();
} }
}); });
it('escaped single quote notation', (done) => {
let result = jsonpath.select({"single'quote":"value"}, "$['single\\'quote']");
if (JSON.stringify(result) === JSON.stringify(["value"])) {
done();
}
});
it('escaped double quote notation', (done) => {
let result = jsonpath.select({"single\"quote":"value"}, "$['single\"quote']");
if (JSON.stringify(result) === JSON.stringify(["value"])) {
done();
}
});
it('array range with step - $[::]', (done) => {
let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[::]");
if (JSON.stringify(result) === JSON.stringify(["first", "second", "third", "forth", "fifth"])) {
done();
}
});
it('array range with step - $[::2]', (done) => {
let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[::2]");
if (JSON.stringify(result) === JSON.stringify(["first", "third", "fifth"])) {
done();
}
});
it('array range with step - $[1: :]', (done) => {
let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[1: :]");
if (JSON.stringify(result) === JSON.stringify(["second", "third", "forth", "fifth"])) {
done();
}
});
it('array range with step - $[1:2:]', (done) => {
let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[1:2:]");
if (JSON.stringify(result) === JSON.stringify(["second"])) {
done();
}
});
it('array range with step - $[1::2]', (done) => {
let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[1::2]");
if (JSON.stringify(result) === JSON.stringify(["second", "forth"])) {
done();
}
});
it('array range with step - $[0:3:1]', (done) => {
let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[0:3:1]");
if (JSON.stringify(result) === JSON.stringify(["first", "second", "third"])) {
done();
}
});
it('array range with step - $[0:3:2]', (done) => {
let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[0:3:2]");
if (JSON.stringify(result) === JSON.stringify(["first", "third"])) {
done();
}
});
it('array keys', (done) => {
let result = jsonpath.select({
"key1": "value1",
"key2": 2
}, "$['key1', 'key2']");
if (JSON.stringify(result) === JSON.stringify(["value1", 2])) {
done();
}
});
}); });
describe('SelectorMut test', () => { describe('SelectorMut test', () => {
@ -821,4 +894,13 @@ describe('README test', () => {
done(); done();
} }
}); });
});
describe('ISSUE test', () => {
it('Results do not match other implementations #6', (done) => {
let result = jsonpath.select(["first", "second"], "$[:]");
if (JSON.stringify(result) === JSON.stringify(["first", "second"])) {
done();
}
});
}); });