NodeJs 0.1.7 - support Selector(0.1.9)

This commit is contained in:
freestrings 2019-04-09 16:13:09 +09:00
parent ceadbcec84
commit d263e30c91
6 changed files with 701 additions and 79 deletions

View File

@ -12,14 +12,52 @@ Build from source instead of using pre-built binary, and if Rust is not installe
> Not yet tested in Windows > Not yet tested in Windows
## 목차 ## APIs
* [jsonpath.Selector](#jsonpathselector)
* [jsonpath.select(json: string|object, jsonpath: string)](#json-stringobject-jsonpath-string) * [jsonpath.select(json: string|object, jsonpath: string)](#json-stringobject-jsonpath-string)
* [jsonpath.compile(jsonpath: string)](#compilejsonpath-string) * [jsonpath.compile(jsonpath: string)](#compilejsonpath-string)
* [jsonpath.selector(json: string|object)](#selectorjson-stringobject) * [jsonpath.selector(json: string|object)](#selectorjson-stringobject)
* [Simple time check](https://github.com/freestrings/jsonpath/wiki/Simple-timecheck-jsonpath-native) * [Simple time check](https://github.com/freestrings/jsonpath/wiki/Simple-timecheck-jsonpath-native)
* [Other Examples](https://github.com/freestrings/jsonpath/wiki/Javascript-examples) * [Other Examples](https://github.com/freestrings/jsonpath/wiki/Javascript-examples)
### jsonpath.Selector
```javascript
let jsonObj = {
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]
};
let selector = new jsonpath.Selector().value(jsonObj);
{
let jsonObj = selector.path('$..[?(@.age >= 30)]').selectTo();
let resultObj = [{"name": "친구3", "age": 30}];
console.log(JSON.stringify(jsonObj) === JSON.stringify(resultObj));
}
{
let jsonObj = selector.path('$..[?(@.age == 20)]').selectTo();
let resultObj = [{"name": "친구1", "age": 20}, {"name": "친구2", "age": 20}];
console.log(JSON.stringify(jsonObj) === JSON.stringify(resultObj));
}
{
let jsonObj = selector.value({"friends": [ {"name": "친구5", "age": 20} ]}).selectTo();
let resultObj = [{"name": "친구5", "age": 20}];
console.log(JSON.stringify(jsonObj) === JSON.stringify(resultObj));
}
```
### jsonpath.select(json: string|object, jsonpath: string) ### jsonpath.select(json: string|object, jsonpath: string)
```javascript ```javascript

View File

@ -1,7 +1,7 @@
const { Compile, Selector, selectStr } = require('../native'); const { CompileFn, SelectorFn, selectStr, Selector: _Selector } = require('../native');
function compile(path) { function compile(path) {
let compile = new Compile(path); let compile = new CompileFn(path);
return (json) => { return (json) => {
if(typeof json != 'string') { if(typeof json != 'string') {
json = JSON.stringify(json) json = JSON.stringify(json)
@ -14,9 +14,9 @@ function selector(json) {
if(typeof json != 'string') { if(typeof json != 'string') {
json = JSON.stringify(json) json = JSON.stringify(json)
} }
let selector = new Selector(json); let selector = new SelectorFn(json);
return (path) => { return (path) => {
return JSON.parse(selector.selector(path)); return JSON.parse(selector.select(path));
} }
} }
@ -27,8 +27,38 @@ function select(json, path) {
return JSON.parse(selectStr(json, path)); return JSON.parse(selectStr(json, path));
} }
class Selector {
constructor() {
this._selector = new _Selector();
return this;
}
path(path) {
this._selector.path(path);
return this;
}
value(json) {
if(typeof json != 'string') {
json = JSON.stringify(json)
}
this._selector.value_from_str(json);
return this;
}
selectToStr() {
return this._selector.select_to_str();
}
selectTo() {
return JSON.parse(this.selectToStr());
}
}
module.exports = { module.exports = {
compile, compile,
selector, selector,
select select,
Selector
}; };

View File

@ -14,7 +14,7 @@ exclude = ["artifacts.json", "index.node"]
neon-build = "0.2.0" neon-build = "0.2.0"
[dependencies] [dependencies]
jsonpath_lib = "0.1.8" jsonpath_lib = "0.1.9"
neon = "0.2.0" neon = "0.2.0"
neon-serde = "0.1.1" neon-serde = "0.1.1"
serde_json = { version = "1.0", features = ["preserve_order"] } serde_json = { version = "1.0", features = ["preserve_order"] }

View File

@ -4,13 +4,13 @@ extern crate neon;
extern crate neon_serde; extern crate neon_serde;
extern crate serde_json; extern crate serde_json;
use std::ops::Deref;
use jsonpath::filter::value_filter::JsonValueFilter; use jsonpath::filter::value_filter::JsonValueFilter;
use jsonpath::parser::parser::{Node, NodeVisitor, Parser}; use jsonpath::parser::parser::{Node, NodeVisitor, Parser};
use jsonpath::ref_value::model::{RefValue, RefValueWrapper}; use jsonpath::ref_value::model::{RefValue, RefValueWrapper};
use jsonpath::Selector;
use neon::prelude::*; use neon::prelude::*;
use serde_json::Value; use serde_json::Value;
use std::ops::Deref;
/// ///
/// `neon_serde::from_value` has very poor performance. /// `neon_serde::from_value` has very poor performance.
@ -35,16 +35,20 @@ fn select_str(mut ctx: FunctionContext) -> JsResult<JsValue> {
} }
} }
pub struct Compile { pub struct CompileFn {
node: Node node: Node
} }
pub struct Selector { pub struct SelectorFn {
json: RefValueWrapper json: RefValueWrapper
} }
pub struct SelectorCls {
selector: Selector
}
declare_types! { declare_types! {
pub class JsCompile for Compile { pub class JsCompileFn for CompileFn {
init(mut ctx) { init(mut ctx) {
let path = ctx.argument::<JsString>(0)?.value(); let path = ctx.argument::<JsString>(0)?.value();
let mut parser = Parser::new(path.as_str()); let mut parser = Parser::new(path.as_str());
@ -54,7 +58,7 @@ declare_types! {
Err(e) => panic!("{:?}", e) Err(e) => panic!("{:?}", e)
}; };
Ok(Compile { node }) Ok(CompileFn { node })
} }
method template(mut ctx) { method template(mut ctx) {
@ -81,7 +85,7 @@ declare_types! {
} }
} }
pub class JsSelector for Selector { pub class JsSelectorFn for SelectorFn {
init(mut ctx) { init(mut ctx) {
let json_str = ctx.argument::<JsString>(0)?.value(); let json_str = ctx.argument::<JsString>(0)?.value();
let ref_value: RefValue = match serde_json::from_str(&json_str) { let ref_value: RefValue = match serde_json::from_str(&json_str) {
@ -89,10 +93,10 @@ declare_types! {
Err(e) => panic!("{:?}", e) Err(e) => panic!("{:?}", e)
}; };
Ok(Selector { json: ref_value.into() }) Ok(SelectorFn { json: ref_value.into() })
} }
method selector(mut ctx) { method select(mut ctx) {
let this = ctx.this(); let this = ctx.this();
let json = { let json = {
@ -117,9 +121,55 @@ declare_types! {
} }
} }
} }
pub class JsSelector for SelectorCls {
init(mut _ctx) {
Ok(SelectorCls { selector: Selector::new() })
}
method path(mut ctx) {
let mut this = ctx.this();
let path = ctx.argument::<JsString>(0)?.value();
{
let guard = ctx.lock();
let mut this = this.borrow_mut(&guard);
let _ = this.selector.path(&path);
}
Ok(JsUndefined::new().upcast())
}
method value_from_str(mut ctx) {
let mut this = ctx.this();
let json_str = ctx.argument::<JsString>(0)?.value();
{
let guard = ctx.lock();
let mut this = this.borrow_mut(&guard);
let _ = this.selector.value_from_str(&json_str);
}
Ok(JsUndefined::new().upcast())
}
method select_to_str(mut ctx) {
let mut this = ctx.this();
let result = {
let guard = ctx.lock();
let mut this = this.borrow_mut(&guard);
this.selector.select_to_str()
};
match result {
Ok(json_str) => Ok(JsString::new(&mut ctx, &json_str).upcast()),
Err(e) => panic!("{:?}", e)
}
}
}
} }
register_module!(mut m, { register_module!(mut m, {
m.export_class::<JsCompile>("Compile").expect("Compile class error"); m.export_class::<JsCompileFn>("CompileFn").expect("CompileFn class error");
m.export_class::<JsSelectorFn>("SelectorFn").expect("SelectorFn class error");
m.export_class::<JsSelector>("Selector").expect("Selector class error"); m.export_class::<JsSelector>("Selector").expect("Selector class error");
m.export_function("select", select)?; m.export_function("select", select)?;
m.export_function("selectStr", select_str)?; m.export_function("selectStr", select_str)?;

View File

@ -1,11 +1,12 @@
{ {
"name": "jsonpath-rs", "name": "jsonpath-rs",
"version": "0.1.6", "version": "0.1.7",
"description": "It is JsonPath implementation. The core implementation is written in Rust", "description": "It is JsonPath implementation. The core implementation is written in Rust",
"author": "Changseok Han <freestrings@gmail.com>", "author": "Changseok Han <freestrings@gmail.com>",
"license": "MIT", "license": "MIT",
"keywords": [ "keywords": [
"jsonpath", "jsonpath",
"rust-addon",
"rust-binding", "rust-binding",
"rust", "rust",
"rustlang", "rustlang",

View File

@ -1,5 +1,360 @@
const jsonpath = require('../lib/index.js'); const jsonpath = require('../lib/index.js');
let jsonObj = {
"store": {
"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
}
},
"expensive": 10
};
let list = {
'$.store.book[*].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": "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
}
],
'$.store..price':[
8.95,
12.99,
8.99,
22.99,
19.95
],
'$..book[2]': [
{
"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
}
],
'$..book[0,1]': [
{
"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
}
],
'$..book[:2]': [
{
"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
}
],
'$..book[1:2]': [
{
"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": "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": "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": "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": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99
}
],
'$..*': [
{
"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
],
'$..book[ ?( (@.price < 13 || $.store.bicycle.price < @.price) && @.price <=10 ) ]': [
{
"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
}
]
};
describe('compile test', () => { describe('compile test', () => {
it('basic', (done) => { it('basic', (done) => {
let template = jsonpath.compile('$.a'); let template = jsonpath.compile('$.a');
@ -30,70 +385,218 @@ describe('select test', () => {
}); });
describe('filter test', () => { describe('filter test', () => {
it('complex filter1', (done) => {
let json = {
'store': {
'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,
},
},
'expensive': 10,
};
let target = [ function run(done, path, expected) {
{ let result = jsonpath.select(jsonObj, path);
category: 'fiction', if (JSON.stringify(result) === JSON.stringify(expected)) {
author: 'Evelyn Waugh', done();
title: 'Sword of Honour', }
price: 12.99, }
},
{
category: 'fiction',
author: 'J. R. R. Tolkien',
title: 'The Lord of the Rings',
isbn: '0-395-19395-8',
price: 22.99,
},
{
category: 'reference',
author: 'Nigel Rees',
title: 'Sayings of the Century',
price: 8.95,
}]
;
let result = jsonpath.select(json, '$..book[?((@.price == 12.99 || $.store.bicycle.price < @.price) || @.category == "reference")]'); for( var i in list ) {
if (JSON.stringify(result) === JSON.stringify(target)) { it(i, (done) => {
run (done, i, list[i]);
})
}
});
describe('Selector test', () => {
it('basic selectTo', (done) => {
let result = new jsonpath.Selector().path('$.a').value({'a': 1}).selectTo();
if (result === 1) {
done(); done();
} }
}); });
it('basic selectToStr', (done) => {
let result = new jsonpath.Selector().path('$.a').value({'a': 1}).selectToStr();
if (result === '1') {
done();
}
});
it('select', (done) => {
let selector = new jsonpath.Selector().value(jsonObj);
for(var i in list) {
if(JSON.stringify(list[i]) !== selector.path(i).selectToStr()) {
throw `fail: ${i}`;
}
}
done();
});
});
describe('README test', () => {
it('jsonpath.Selector', (done) => {
let jsonObj = {
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]
};
let selector = new jsonpath.Selector().value(jsonObj);
{
let jsonObj = selector.path('$..[?(@.age >= 30)]').selectTo();
let resultObj = [{"name": "친구3", "age": 30}];
if(JSON.stringify(jsonObj) !== JSON.stringify(resultObj)) {
throw 'jsonpath.Selector: $..[?(@.age >= 30)]';
}
}
{
let jsonObj = selector.path('$..[?(@.age == 20)]').selectTo();
let resultObj = [{"name": "친구1", "age": 20}, {"name": "친구2", "age": 20}];
if(JSON.stringify(jsonObj) !== JSON.stringify(resultObj)) {
throw 'jsonpath.Selector: $..[?(@.age >= 20)]';
}
}
{
let jsonObj = selector.value({"friends": [ {"name": "친구5", "age": 20} ]}).selectTo();
let resultObj = [{"name": "친구5", "age": 20}];
if(JSON.stringify(jsonObj) !== JSON.stringify(resultObj)) {
throw 'jsonpath.Selector: change value';
}
}
done();
});
it('jsonpath.select(json: string|object, jsonpath: string)', (done) => {
let jsonObj = {
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]
};
let ret = [
{"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(
JSON.stringify(ret) !== JSON.stringify(selectAsString) ||
JSON.stringify(ret) !== JSON.stringify(selectAsObj)
) {
throw 'jsonpath.select(json: string|object, jsonpath: string)';
}
done();
});
it('jsonpath.compile(jsonpath: string)', (done) => {
let template = jsonpath.compile('$..friends[0]');
let jsonObj = {
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]
};
let ret = [
{"name": "친구3", "age": 30},
{"name": "친구1", "age": 20}
];
let selectAsString = template(JSON.stringify(jsonObj));
let selectAsObj = template(jsonObj);
if(
JSON.stringify(ret) !== JSON.stringify(selectAsString) ||
JSON.stringify(ret) !== JSON.stringify(selectAsObj)
) {
throw 'jsonpath.compile(jsonpath: string) 1';
}
let jsonObj2 = {
"school": {
"friends": [
{"name": "Millicent Norman"},
{"name": "Vincent Cannon"}
]
},
"friends": [ {"age": 30}, {"age": 40} ]
};
let ret2 = [
{"age": 30},
{"name": "Millicent Norman"}
];
let selectAsString2 = template(JSON.stringify(jsonObj2));
let selectAsObj2 = template(jsonObj2);
if(
JSON.stringify(ret2) !== JSON.stringify(selectAsString2) ||
JSON.stringify(ret2) !== JSON.stringify(selectAsObj2)
) {
throw 'jsonpath.compile(jsonpath: string) 2';
}
done();
});
it('jsonpath.selector(json: string|object)', (done) => {
let jsonObj = {
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]
};
let ret1 = [
{"name": "친구3", "age": 30},
{"name": "친구1", "age": 20}
];
let ret2 = [
{"name": "친구4"},
{"name": "친구2", "age": 20}
];
let selector = jsonpath.selector(jsonObj);
let select1 = selector('$..friends[0]');
let select2 = selector('$..friends[1]');
if(
JSON.stringify(ret1) !== JSON.stringify(select1) ||
JSON.stringify(ret2) !== JSON.stringify(select2)
) {
throw 'jsonpath.selector(json: string|object)';
}
done();
});
}); });