diff --git a/build-wasm.sh b/build-wasm.sh new file mode 100755 index 0000000..fe7039a --- /dev/null +++ b/build-wasm.sh @@ -0,0 +1,104 @@ +#!/bin/bash + +set -e + +# project_root +DIR="$(pwd)" +WASM="${DIR}"/wasm +WASM_WWW="${WASM}"/www +WASM_WWW_BENCH="${WASM}"/www_bench +WASM_BROWSER_PKG="${WASM}"/browser_pkg +WASM_NODEJS_PKG="${WASM}"/nodejs_pkg +WASM_ALL_PKG="${WASM}"/all_pkg +WASM_TEST="${WASM}"/tests +DOCS="${DIR}"/docs +DOCS_BENCH="${DOCS}"/bench + +__msg () { + echo ">>>>>>>>>>$1<<<<<<<<<<" +} + +__cargo_clean () { + cd "${WASM}" && cargo clean && \ + cd "${DIR}" && cargo clean +} + +echo +__msg "clean wasm" +rm -rf \ + "${WASM_NODEJS_PKG}" \ + "${WASM_BROWSER_PKG}" \ + "${WASM_ALL_PKG}" \ + "${WASM_WWW}"/node_modules \ + "${WASM_WWW_BENCH}"/node_modules \ + "${WASM_WWW}"/dist \ + "${WASM_WWW_BENCH}"/dist \ + "${WASM_TEST}"/node_modules + +if [ "$1" = "all" ]; then + __msg "clean all wasm" + __cargo_clean +fi + +__msg "npm install: wasm" +cd "${WASM_WWW}" && npm install +__msg "npm install: wasm_bench" +cd "${WASM_WWW_BENCH}" && npm install +__msg "npm install: wasm test" +cd "${WASM_TEST}" && npm install + +echo +echo +__msg "wasm-pack" +cd "${WASM}" && \ + wasm-pack build --release --target=nodejs --out-dir "${WASM_NODEJS_PKG}" + +cd "${WASM}" && \ + wasm-pack build --release --target=browser --out-dir "${WASM_BROWSER_PKG}" +# && \ +# wasm-pack test --chrome --firefox --headless + +__msg "wasm npm packaging" +cp -r "${WASM_BROWSER_PKG}" "${WASM_ALL_PKG}/" && \ + sed "s/require[\(]'\.\/jsonpath_wasm_bg/require\('\.\/jsonpath_wasm_nodejs/" "${WASM_NODEJS_PKG}/jsonpath_wasm.js" \ + > "${WASM_ALL_PKG}/jsonpath_wasm_main.js" && \ + sed "s/require[\(]'\.\/jsonpath_wasm/require\('\.\/jsonpath_wasm_main/" "${WASM_NODEJS_PKG}/jsonpath_wasm_bg.js" \ + > "${WASM_ALL_PKG}/jsonpath_wasm_nodejs.js" && \ + jq ".files += [\"jsonpath_wasm_nodejs.js\"]" ${WASM_ALL_PKG}/package.json \ + | jq ".main = \"jsonpath_wasm_main.js\"" \ + | jq ".keywords += [\"jsonpath\", \"json\", \"webassembly\", \"parsing\", \"rust\"]" \ + > ${WASM_ALL_PKG}/temp.json && \ + mv -v "${WASM_ALL_PKG}/temp.json" "${WASM_ALL_PKG}/package.json" && \ + cd "${WASM_ALL_PKG}" && npm link + +echo +__msg "link" +cd "${WASM_WWW}" && \ + npm link jsonpath-wasm + +cd "${WASM_WWW_BENCH}" && \ + npm link jsonpath-wasm + +cd "${WASM_TEST}" && \ + npm link jsonpath-wasm + +echo +echo +__msg "wasm test" +cd "${WASM_TEST}" && npm test + +if [ "$1" = "all" ] || [ "$1" = "docs" ]; then + echo + __msg "docs" + cd "${WASM_WWW}" && \ + npm run build && + rm -f "${DOCS}"/*.js "${DOCS}"/*.wasm "${DOCS}"/*.html && \ + cp "${WASM_WWW}"/dist/*.* "${DOCS}"/ + + cd "${WASM_WWW_BENCH}" && \ + npm run build && + rm -f "${DOCS_BENCH}"/*.js "${DOCS_BENCH}"/*.wasm "${DOCS_BENCH}"/*.html && \ + cp "${WASM_WWW_BENCH}"/dist/*.* "${DOCS_BENCH}"/ +fi + +__msg "wasm done" \ No newline at end of file diff --git a/build.sh b/build.sh index 446fc36..70d0d7f 100755 --- a/build.sh +++ b/build.sh @@ -120,4 +120,4 @@ cd "${WASM_WWW_BENCH}" && \ rm -f "${DOCS_BENCH}"/*.js "${DOCS_BENCH}"/*.wasm "${DOCS_BENCH}"/*.html && \ cp "${WASM_WWW_BENCH}"/dist/*.* "${DOCS_BENCH}"/ -__msg "done" +__msg "done" \ No newline at end of file diff --git a/src/select/mod.rs b/src/select/mod.rs index af57cb5..e1581bd 100644 --- a/src/select/mod.rs +++ b/src/select/mod.rs @@ -1,10 +1,10 @@ use std::collections::HashSet; use array_tool::vec::{Intersect, Union}; +use indexmap::IndexSet; use serde_json::{Number, Value}; use parser::parser::*; -use indexmap::IndexSet; fn to_f64(n: &Number) -> f64 { if n.is_i64() { @@ -1088,10 +1088,14 @@ impl SelectorMut { let target_opt = match *target_once { Value::Object(ref mut map) => { if is_last { - if let Some(v) = map.remove(token) { - map.insert(token.clone(), fun(&v)); + let v = if let Some(v) = map.get(token) { + fun(v) + } else { return; - } + }; + + map.insert(token.clone(), v); + return; } map.get_mut(token) } diff --git a/tests/common.rs b/tests/common.rs index ecd25c0..a2884f7 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -8,6 +8,7 @@ use serde_json::Value; use self::jsonpath::Selector; +#[allow(dead_code)] pub fn setup() { let _ = env_logger::try_init(); } diff --git a/tests/readme.rs b/tests/readme.rs index f328c79..ef98673 100644 --- a/tests/readme.rs +++ b/tests/readme.rs @@ -8,6 +8,8 @@ use serde_json::Value; use jsonpath::{Selector, SelectorMut}; +mod common; + #[test] fn readme() { let json_obj = json!({ @@ -407,6 +409,26 @@ fn readme_delete() { ]})); } +#[test] +fn readme_delete2() { + let json_obj = common::read_json("./benches/example.json"); + + let ret = jsonpath::delete(json_obj, "$.store.book").unwrap(); + + println!("{:?}", ret); + + assert_eq!(ret, json!({ + "store": { + "book": null, + "bicycle": { + "color": "red", + "price": 19.95 + } + }, + "expensive": 10 + })); +} + #[test] fn readme_replace_with() { let json_obj = json!({ diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index 1288afb..530827e 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -1,17 +1,14 @@ extern crate cfg_if; -extern crate core; extern crate js_sys; extern crate jsonpath_lib as jsonpath; -extern crate serde; extern crate serde_json; extern crate wasm_bindgen; -extern crate web_sys; use cfg_if::cfg_if; use jsonpath::{JsonPathError, Parser}; use jsonpath::Selector as _Selector; +use jsonpath::SelectorMut as _SelectorMut; use serde_json::Value; -use wasm_bindgen::*; use wasm_bindgen::prelude::*; cfg_if! { @@ -32,6 +29,16 @@ cfg_if! { } } +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(js_namespace = console)] + fn error(s: &str); +} + +macro_rules! console_error { + ($($t:tt)*) => (error(&format_args!($($t)*).to_string())) +} + fn into_serde_json(js_value: &JsValue) -> Result where D: for<'a> serde::de::Deserialize<'a> { @@ -48,6 +55,32 @@ fn into_serde_json(js_value: &JsValue) -> Result } } +fn replace_fun(v: &Value, fun: &js_sys::Function) -> Value { + match JsValue::from_serde(v) { + Ok(js_v) => { + match fun.call1(&JsValue::NULL, &js_v) { + Ok(result) => { + match into_serde_json(&result) { + Ok(json) => json, + Err(e) => { + console_error!("replace_with - closure returned a invalid JSON: {:?}", e); + Value::Null + } + } + } + Err(e) => { + console_error!("replace_with - fail to call closure: {:?}", e); + Value::Null + } + } + } + Err(e) => { + console_error!("replace_with - invalid JSON object: {:?}", e); + Value::Null + } + } +} + #[wasm_bindgen] pub fn compile(path: &str) -> JsValue { let node = Parser::compile(path); @@ -107,15 +140,46 @@ pub fn selector(js_value: JsValue) -> JsValue { #[wasm_bindgen] pub fn select(js_value: JsValue, path: &str) -> JsValue { - let mut selector = _Selector::new(); - let _ = selector.str_path(path); - let json = match into_serde_json(&js_value) { Ok(json) => json, Err(e) => return JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e))) }; - match selector.value(&json).select() { + match jsonpath::select(&json, path) { + Ok(ret) => match JsValue::from_serde(&ret) { + Ok(ret) => ret, + Err(e) => JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e.to_string()))) + }, + Err(e) => JsValue::from_str(&format!("{:?}", e)) + } +} + +#[wasm_bindgen(catch, js_name = "deleteValue")] +pub fn delete(js_value: JsValue, path: &str) -> JsValue { + let json = match into_serde_json(&js_value) { + Ok(json) => json, + Err(e) => return JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e))) + }; + + match jsonpath::delete(json, path) { + Ok(ret) => { + match JsValue::from_serde(&ret) { + Ok(ret) => ret, + Err(e) => JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e.to_string()))) + } + } + Err(e) => JsValue::from_str(&format!("{:?}", e)) + } +} + +#[wasm_bindgen(catch, js_name = "replaceWith")] +pub fn replace_with(js_value: JsValue, path: &str, fun: js_sys::Function) -> JsValue { + let json = match into_serde_json(&js_value) { + Ok(json) => json, + Err(e) => return JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e))) + }; + + match jsonpath::replace_with(json, path, &mut |v| replace_fun(v, &fun)) { Ok(ret) => match JsValue::from_serde(&ret) { Ok(ret) => ret, Err(e) => JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e.to_string()))) @@ -179,4 +243,96 @@ impl Selector { Err(e) => Err(JsValue::from_str(&format!("{:?}", e))) } } +} + +/// +/// `wasm_bindgen` 제약으로 builder-pattern을 구사 할 수 없다. +/// +#[wasm_bindgen] +pub struct SelectorMut { + path: Option, + value: Option, +} + +#[wasm_bindgen] +impl SelectorMut { + #[wasm_bindgen(constructor)] + pub fn new() -> Self { + SelectorMut { path: None, value: None } + } + + #[wasm_bindgen(catch)] + pub fn path(&mut self, path: &str) -> Result<(), JsValue> { + self.path = Some(path.to_string()); + Ok(()) + } + + #[wasm_bindgen(catch)] + pub fn value(&mut self, value: JsValue) -> Result<(), JsValue> { + let json = into_serde_json(&value) + .map_err(|e| JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e))))?; + self.value = Some(json); + Ok(()) + } + + #[wasm_bindgen(catch, js_name = "deleteValue")] + pub fn delete(&mut self) -> Result<(), JsValue> { + let mut selector = _SelectorMut::new(); + + if let Some(path) = &self.path { + let _ = selector.str_path(path); + } else { + return Err(JsValue::from_str(&format!("{:?}", JsonPathError::EmptyPath))); + }; + + if let Some(value) = self.value.take() { + selector.value(value); + } else { + return Err(JsValue::from_str(&format!("{:?}", JsonPathError::EmptyValue))); + }; + + match selector.delete() { + Err(e) => Err(JsValue::from_str(&format!("{:?}", e))), + _ => { + self.value = selector.take(); + Ok(()) + } + } + } + + #[wasm_bindgen(catch, js_name = replaceWith)] + pub fn replace_with(&mut self, fun: js_sys::Function) -> Result<(), JsValue> { + let mut selector = _SelectorMut::new(); + + if let Some(path) = &self.path { + let _ = selector.str_path(path); + } else { + return Err(JsValue::from_str(&format!("{:?}", JsonPathError::EmptyPath))); + }; + + if let Some(value) = self.value.take() { + selector.value(value); + } else { + return Err(JsValue::from_str(&format!("{:?}", JsonPathError::EmptyValue))); + }; + + match selector.replace_with(&mut |v| replace_fun(v, &fun)) { + Err(e) => Err(JsValue::from_str(&format!("{:?}", e))), + _ => { + self.value = selector.take(); + Ok(()) + } + } + } + + #[wasm_bindgen(catch)] + pub fn take(&mut self) -> Result { + match self.value.take() { + Some(ret) => match JsValue::from_serde(&ret) { + Ok(ret) => Ok(ret), + Err(e) => Err(JsValue::from_str(&format!("{:?}", e))) + }, + None => Err(JsValue::from_str(&format!("{:?}", JsonPathError::EmptyValue))) + } + } } \ No newline at end of file diff --git a/wasm/tests/test/index.spec.js b/wasm/tests/test/index.spec.js index 8af3495..c17bcbd 100644 --- a/wasm/tests/test/index.spec.js +++ b/wasm/tests/test/index.spec.js @@ -1,358 +1,358 @@ const jsonpath = require('jsonpath-wasm'); let jsonObj = { - "store": { - "book": [ + 'store': { + 'book': [ { - "category": "reference", - "author": "Nigel Rees", - "title": "Sayings of the Century", - "price": 8.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': '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': '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 - } + '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 - } + 'bicycle': { + 'color': 'red', + 'price': 19.95, + }, }, - "expensive": 10 + 'expensive': 10, }; let list = { '$.store.book[*].author': [ - "Nigel Rees", - "Evelyn Waugh", - "Herman Melville", - "J. R. R. Tolkien" + 'Nigel Rees', + 'Evelyn Waugh', + 'Herman Melville', + 'J. R. R. Tolkien', ], - '$..author':[ - "Nigel Rees", - "Evelyn Waugh", - "Herman Melville", - "J. R. R. Tolkien" + '$..author': [ + 'Nigel Rees', + 'Evelyn Waugh', + 'Herman Melville', + 'J. R. R. Tolkien', ], '$.store.*': [ [ { - "category": "reference", - "author": "Nigel Rees", - "title": "Sayings of the Century", - "price": 8.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': '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': '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 - } + '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 - } + 'color': 'red', + 'price': 19.95, + }, ], - '$.store..price':[ + '$.store..price': [ 8.95, 12.99, 8.99, 22.99, - 19.95 + 19.95, ], '$..book[2]': [ { - "category": "fiction", - "author": "Herman Melville", - "title": "Moby Dick", - "isbn": "0-553-21311-3", - "price": 8.99 - } + 'category': 'fiction', + 'author': 'Herman Melville', + 'title': 'Moby Dick', + 'isbn': '0-553-21311-3', + 'price': 8.99, + }, ], '$..book[-2]': [ { - "category": "fiction", - "author": "Herman Melville", - "title": "Moby Dick", - "isbn": "0-553-21311-3", - "price": 8.99 - } + 'category': 'fiction', + 'author': 'Herman Melville', + 'title': 'Moby Dick', + 'isbn': '0-553-21311-3', + 'price': 8.99, + }, ], '$..book[0,1]': [ { - "category": "reference", - "author": "Nigel Rees", - "title": "Sayings of the Century", - "price": 8.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': 'Evelyn Waugh', + 'title': 'Sword of Honour', + 'price': 12.99, + }, ], '$..book[:2]': [ { - "category": "reference", - "author": "Nigel Rees", - "title": "Sayings of the Century", - "price": 8.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': 'Evelyn Waugh', + 'title': 'Sword of Honour', + 'price': 12.99, + }, ], '$..book[1:2]': [ { - "category": "fiction", - "author": "Evelyn Waugh", - "title": "Sword of Honour", - "price": 12.99 - } + 'category': 'fiction', + 'author': 'Evelyn Waugh', + 'title': 'Sword of Honour', + 'price': 12.99, + }, ], '$..book[-2:]': [ { - "category": "fiction", - "author": "Herman Melville", - "title": "Moby Dick", - "isbn": "0-553-21311-3", - "price": 8.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 - } + 'category': 'fiction', + 'author': 'J. R. R. Tolkien', + 'title': 'The Lord of the Rings', + 'isbn': '0-395-19395-8', + 'price': 22.99, + }, ], '$..book[2:]': [ { - "category": "fiction", - "author": "Herman Melville", - "title": "Moby Dick", - "isbn": "0-553-21311-3", - "price": 8.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 - } + 'category': 'fiction', + 'author': 'J. R. R. Tolkien', + 'title': 'The Lord of the Rings', + 'isbn': '0-395-19395-8', + 'price': 22.99, + }, ], '$..book[?(@.isbn)]': [ { - "category": "fiction", - "author": "Herman Melville", - "title": "Moby Dick", - "isbn": "0-553-21311-3", - "price": 8.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 - } + 'category': 'fiction', + 'author': 'J. R. R. Tolkien', + 'title': 'The Lord of the Rings', + 'isbn': '0-395-19395-8', + 'price': 22.99, + }, ], '$.store.book[?(@.price < 10)]': [ { - "category": "reference", - "author": "Nigel Rees", - "title": "Sayings of the Century", - "price": 8.95 + '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 - } + 'category': 'fiction', + 'author': 'Herman Melville', + 'title': 'Moby Dick', + 'isbn': '0-553-21311-3', + 'price': 8.99, + }, ], '$..*': [ { - "book": [ + 'book': [ { - "category": "reference", - "author": "Nigel Rees", - "title": "Sayings of the Century", - "price": 8.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': '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': '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 - } + '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 - } + 'bicycle': { + 'color': 'red', + 'price': 19.95, + }, }, 10, [ { - "category": "reference", - "author": "Nigel Rees", - "title": "Sayings of the Century", - "price": 8.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': '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': '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 - } + '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 + 'color': 'red', + 'price': 19.95, }, { - "category": "reference", - "author": "Nigel Rees", - "title": "Sayings of the Century", - "price": 8.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': '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': '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 + '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", + 'reference', + 'Nigel Rees', + 'Sayings of the Century', 8.95, - "fiction", - "Evelyn Waugh", - "Sword of Honour", + 'fiction', + 'Evelyn Waugh', + 'Sword of Honour', 12.99, - "fiction", - "Herman Melville", - "Moby Dick", - "0-553-21311-3", + '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", + 'fiction', + 'J. R. R. Tolkien', + 'The Lord of the Rings', + '0-395-19395-8', 22.99, - "red", - 19.95 + 'red', + 19.95, ], '$..book[ ?( (@.price < 13 || $.store.bicycle.price < @.price) && @.price <=10 ) ]': [ { - "category": "reference", - "author": "Nigel Rees", - "title": "Sayings of the Century", - "price": 8.95 + '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 - } - ] + 'category': 'fiction', + 'author': 'Herman Melville', + 'title': 'Moby Dick', + 'isbn': '0-553-21311-3', + 'price': 8.99, + }, + ], }; describe('compile test', () => { @@ -393,34 +393,132 @@ describe('filter test', () => { } } - for( var i in list ) { + for (var i in list) { it(i, (done) => { - 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} + 'a': 1, + 'b': {'a': 1}, + 'c': {'a': 1}, }); let result = selector.select(); - if (JSON.stringify(result) === JSON.stringify([ {"a": 1}, {"a": 1} ])) { + if (JSON.stringify(result) === JSON.stringify([{'a': 1}, {'a': 1}])) { done(); } }); }); +describe('SelectorMut test', () => { + it('delete', (done) => { + let jsonObjNew = JSON.parse(JSON.stringify(jsonObj)); + let result = jsonpath.deleteValue(jsonObjNew, '$.store.book'); + if (JSON.stringify(result) === JSON.stringify({ + 'store': { + 'book': null, + 'bicycle': { + 'color': 'red', + 'price': 19.95, + }, + }, + 'expensive': 10, + })) { + done(); + } + }); + + it('replaceWith', (done) => { + let jsonObjNew = JSON.parse(JSON.stringify(jsonObj)); + let result = jsonpath.replaceWith(jsonObjNew, '$.store.book', (v) => { + let ret = v[0]; + ret.price = 9; + return ret; + }); + if (JSON.stringify(result) === JSON.stringify({ + 'store': { + 'book': { + 'category': 'reference', + 'author': 'Nigel Rees', + 'title': 'Sayings of the Century', + 'price': 9, + }, + 'bicycle': { + 'color': 'red', + 'price': 19.95, + }, + }, + 'expensive': 10, + })) { + done(); + } + }); + + it('SeletorMut delete', (done) => { + let jsonObjNew = JSON.parse(JSON.stringify(jsonObj)); + let selector = new jsonpath.SelectorMut(); + selector.path('$.store.book'); + selector.value(jsonObjNew); + selector.deleteValue(); + + let result = selector.take(); + if (JSON.stringify(result) === JSON.stringify({ + 'store': { + 'book': null, + 'bicycle': { + 'color': 'red', + 'price': 19.95, + }, + }, + 'expensive': 10, + })) { + done(); + } + }) + + it('SeletorMut replaceWith', (done) => { + let jsonObjNew = JSON.parse(JSON.stringify(jsonObj)); + let selector = new jsonpath.SelectorMut(); + selector.path('$.store.book'); + selector.value(jsonObjNew); + selector.replaceWith((v) => { + let ret = v[0]; + ret.price = 9; + return ret; + }); + + let result = selector.take(); + if (JSON.stringify(result) === JSON.stringify({ + 'store': { + 'book': { + 'category': 'reference', + 'author': 'Nigel Rees', + 'title': 'Sayings of the Century', + 'price': 9, + }, + 'bicycle': { + 'color': 'red', + 'price': 19.95, + }, + }, + 'expensive': 10, + })) { + done(); + } + }) +}); + describe('Selector test', () => { it('select', (done) => { let selector = new jsonpath.Selector(); selector.value(jsonObj); - for(var i in list) { + for (var i in list) { selector.path(i); - if(JSON.stringify(list[i]) !== JSON.stringify(selector.select())) { + if (JSON.stringify(list[i]) !== JSON.stringify(selector.select())) { throw `fail: ${i}`; } } @@ -431,16 +529,16 @@ describe('Selector test', () => { describe('README test', () => { it('jsonpath.Selector', (done) => { let jsonObj = { - "school": { - "friends": [ - {"name": "친구1", "age": 20}, - {"name": "친구2", "age": 20} - ] + 'school': { + 'friends': [ + {'name': '친구1', 'age': 20}, + {'name': '친구2', 'age': 20}, + ], }, - "friends": [ - {"name": "친구3", "age": 30}, - {"name": "친구4"} - ] + 'friends': [ + {'name': '친구3', 'age': 30}, + {'name': '친구4'}, + ], }; let selector = new jsonpath.Selector(); @@ -449,8 +547,8 @@ describe('README test', () => { { selector.path('$..[?(@.age >= 30)]'); let jsonObj = selector.select(); - let resultObj = [{"name": "친구3", "age": 30}]; - if(JSON.stringify(jsonObj) !== JSON.stringify(resultObj)) { + let resultObj = [{'name': '친구3', 'age': 30}]; + if (JSON.stringify(jsonObj) !== JSON.stringify(resultObj)) { throw 'jsonpath.Selector: $..[?(@.age >= 30)]'; } } @@ -458,17 +556,17 @@ describe('README test', () => { { selector.path('$..[?(@.age == 20)]'); let jsonObj = selector.select(); - let resultObj = [{"name": "친구1", "age": 20}, {"name": "친구2", "age": 20}]; - if(JSON.stringify(jsonObj) !== JSON.stringify(resultObj)) { + let resultObj = [{'name': '친구1', 'age': 20}, {'name': '친구2', 'age': 20}]; + if (JSON.stringify(jsonObj) !== JSON.stringify(resultObj)) { throw 'jsonpath.Selector: $..[?(@.age >= 20)]'; } } { - selector.value({"friends": [ {"name": "친구5", "age": 20} ]}); + selector.value({'friends': [{'name': '친구5', 'age': 20}]}); let jsonObj = selector.select(); - let resultObj = [{"name": "친구5", "age": 20}]; - if(JSON.stringify(jsonObj) !== JSON.stringify(resultObj)) { + let resultObj = [{'name': '친구5', 'age': 20}]; + if (JSON.stringify(jsonObj) !== JSON.stringify(resultObj)) { throw 'jsonpath.Selector: change value'; } } @@ -478,28 +576,27 @@ describe('README test', () => { it('jsonpath.select(json: string|object, jsonpath: string)', (done) => { let jsonObj = { - "school": { - "friends": [ - {"name": "친구1", "age": 20}, - {"name": "친구2", "age": 20} - ] + 'school': { + 'friends': [ + {'name': '친구1', 'age': 20}, + {'name': '친구2', 'age': 20}, + ], }, - "friends": [ - {"name": "친구3", "age": 30}, - {"name": "친구4"} - ] + 'friends': [ + {'name': '친구3', 'age': 30}, + {'name': '친구4'}, + ], }; let ret = [ - {"name": "친구3", "age": 30}, - {"name": "친구1", "age": 20} + {'name': '친구3', 'age': 30}, + {'name': '친구1', 'age': 20}, ]; - let selectAsString = jsonpath.select(JSON.stringify(jsonObj), '$..friends[0]'); let selectAsObj = jsonpath.select(jsonObj, '$..friends[0]'); - if( + if ( JSON.stringify(ret) !== JSON.stringify(selectAsString) || JSON.stringify(ret) !== JSON.stringify(selectAsObj) ) { @@ -513,27 +610,27 @@ describe('README test', () => { let template = jsonpath.compile('$..friends[0]'); let jsonObj = { - "school": { - "friends": [ - {"name": "친구1", "age": 20}, - {"name": "친구2", "age": 20} - ] + 'school': { + 'friends': [ + {'name': '친구1', 'age': 20}, + {'name': '친구2', 'age': 20}, + ], }, - "friends": [ - {"name": "친구3", "age": 30}, - {"name": "친구4"} - ] + 'friends': [ + {'name': '친구3', 'age': 30}, + {'name': '친구4'}, + ], }; let ret = [ - {"name": "친구3", "age": 30}, - {"name": "친구1", "age": 20} + {'name': '친구3', 'age': 30}, + {'name': '친구1', 'age': 20}, ]; let selectAsString = template(JSON.stringify(jsonObj)); let selectAsObj = template(jsonObj); - if( + if ( JSON.stringify(ret) !== JSON.stringify(selectAsString) || JSON.stringify(ret) !== JSON.stringify(selectAsObj) ) { @@ -541,24 +638,24 @@ describe('README test', () => { } let jsonObj2 = { - "school": { - "friends": [ - {"name": "Millicent Norman"}, - {"name": "Vincent Cannon"} - ] + 'school': { + 'friends': [ + {'name': 'Millicent Norman'}, + {'name': 'Vincent Cannon'}, + ], }, - "friends": [ {"age": 30}, {"age": 40} ] + 'friends': [{'age': 30}, {'age': 40}], }; let ret2 = [ - {"age": 30}, - {"name": "Millicent Norman"} + {'age': 30}, + {'name': 'Millicent Norman'}, ]; let selectAsString2 = template(JSON.stringify(jsonObj2)); let selectAsObj2 = template(jsonObj2); - if( + if ( JSON.stringify(ret2) !== JSON.stringify(selectAsString2) || JSON.stringify(ret2) !== JSON.stringify(selectAsObj2) ) { @@ -570,33 +667,33 @@ describe('README test', () => { it('jsonpath.selector(json: string|object)', (done) => { let jsonObj = { - "school": { - "friends": [ - {"name": "친구1", "age": 20}, - {"name": "친구2", "age": 20} - ] + 'school': { + 'friends': [ + {'name': '친구1', 'age': 20}, + {'name': '친구2', 'age': 20}, + ], }, - "friends": [ - {"name": "친구3", "age": 30}, - {"name": "친구4"} - ] + 'friends': [ + {'name': '친구3', 'age': 30}, + {'name': '친구4'}, + ], }; let ret1 = [ - {"name": "친구3", "age": 30}, - {"name": "친구1", "age": 20} + {'name': '친구3', 'age': 30}, + {'name': '친구1', 'age': 20}, ]; let ret2 = [ - {"name": "친구4"}, - {"name": "친구2", "age": 20} + {'name': '친구4'}, + {'name': '친구2', 'age': 20}, ]; let selector = jsonpath.selector(jsonObj); let select1 = selector('$..friends[0]'); let select2 = selector('$..friends[1]'); - if( + if ( JSON.stringify(ret1) !== JSON.stringify(select1) || JSON.stringify(ret2) !== JSON.stringify(select2) ) {