jsonpath/README.md
2019-04-08 15:58:55 +09:00

12 KiB

jsonpath_lib

Build Status crates.io Crates.io npm

Rust 버전 JsonPath 구현이다. WebassemblyJavascript에서도 역시 동일한 API 인터페이스를 제공 한다.

It is an implementation for JsonPath written in Rust. it provide the same API interface in Webassembly and Javascript also.

Why?

To enjoy Rust!

API

With Javascript

With Rust (as library)

Simple time check - webassembly

Simple time check - native addon for NodeJs

With Javascript

jsonpath-wasm library

(not yet published jsonpath-wasm)

// browser
import * as jsonpath from "jsonpath-wasm";
// NodeJs
const jsonpath = require('jsonpath-wasm');

jsonpath-rs library (Only NodeJS)

jsonpath-rs is native addon for NodeJs

const jsonpath = require('jsonpath-rs');

javascript - jsonpath.select(json: string|object, jsonpath: string)

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]');

console.log(
    JSON.stringify(ret) == JSON.stringify(selectAsString),
    JSON.stringify(ret) == JSON.stringify(selectAsObj)
);

// => true, true

javascript - jsonpath.compile(jsonpath: string)

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);

console.log(
    JSON.stringify(ret) == JSON.stringify(selectAsString),
    JSON.stringify(ret) == JSON.stringify(selectAsObj)
);

// => true, true

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);

console.log(
        JSON.stringify(ret2) == JSON.stringify(selectAsString2),
        JSON.stringify(ret2) == JSON.stringify(selectAsObj2)
);

// => true, true

javascript - jsonpath.selector(json: string|object)

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);
// or as json string 
// let selector = jsonpath.selector(JSON.stringify(jsonObj));

let select1 = selector('$..friends[0]');
let select2 = selector('$..friends[1]');

console.log(
    JSON.stringify(ret1) == JSON.stringify(select1),
    JSON.stringify(ret2) == JSON.stringify(select2)
);

// => true, true

javascript - alloc_json, dealloc_json

(not supported in jsonpath-rs)

wasm-bindgen은 Javascript와 Webassembly 간 값을 주고받을 때 JSON 객체는 String으로 변환되기 때문에, 반복해서 사용되는 JSON 객체를 Webassembly 영역에 생성해 두면 성능에 도움이 된다.

Since wasm-bindgen converts JSON objects to String when exchanging values between Javascript and Webassembly, it is helpful to create repeated Json objects in Webassembly area.

const jsonpath = require('@nodejs/jsonpath-wasm');

let jsonObj = {
    "school": {
        "friends": [
            {"name": "친구1", "age": 20},
            {"name": "친구2", "age": 20}
        ]
    },
    "friends": [
        {"name": "친구3", "age": 30},
        {"name": "친구4"}
    ]
};

// allocate jsonObj in webassembly
let ptr = jsonpath.alloc_json(jsonObj);

// `0` is invalid pointer
if(ptr == 0) {
    console.error('invalid ptr'); 
}

let path = '$..friends[0]';
let template = jsonpath.compile(path);
let selector = jsonpath.selector(jsonObj);
// create selector as pointer
let ptrSelector = jsonpath.selector(ptr);

let ret1 = selector(path)
let ret2 = ptrSelector(path)
let ret3 = template(jsonObj);
// select as pointer
let ret4 = template(ptr);
let ret5 = jsonpath.select(jsonObj, path);
// select as pointer
let ret6 = jsonpath.select(ptr, path);

console.log(
    JSON.stringify(ret1) == JSON.stringify(ret2),
    JSON.stringify(ret1) == JSON.stringify(ret3),
    JSON.stringify(ret1) == JSON.stringify(ret4),
    JSON.stringify(ret1) == JSON.stringify(ret5),
    JSON.stringify(ret1) == JSON.stringify(ret6));

// => true true true true true

jsonpath.dealloc_json(ptr);

With Rust (as library)

jsonpath_lib library

extern crate jsonpath_lib as jsonpath;
#[macro_use]
extern crate serde_json;

rust - jsonpath::Selector struct

#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Friend {
    name: String,
    age: Option<u8>,
}

let json_obj = json!({
    "school": {
        "friends": [
            {"name": "친구1", "age": 20},
            {"name": "친구2", "age": 20}
        ]
    },
    "friends": [
        {"name": "친구3", "age": 30},
        {"name": "친구4"}
]});

let mut selector = Selector::new();

let result = selector
    .path("$..[?(@.age >= 30)]").unwrap()
//    .value_from_str(&serde_json::to_string(&json_obj).unwrap() /*&str*/).unwrap()
//    .value_from(&json_obj /*&impl serde::ser::Serialize*/).unwrap()
    .value((&json_obj /*serde_json::value::Value*/ ).into()).unwrap()
    .select_to_value().unwrap();

assert_eq!(json!([{"name": "친구3", "age": 30}]), result);

let result = selector.select_to_str().unwrap();
assert_eq!(r#"[{"name":"친구3","age":30}]"#, result);

let result = selector.select_to::<Vec<Friend>>().unwrap();
assert_eq!(vec![Friend { name: "친구3".to_string(), age: Some(30) }], result);

rust - jsonpath::select(json: &serde_json::value::Value, jsonpath: &str)

let json_obj = json!({
    "school": {
        "friends": [
            {"name": "친구1", "age": 20},
            {"name": "친구2", "age": 20}
        ]
    },
    "friends": [
        {"name": "친구3", "age": 30},
        {"name": "친구4"}
]});

let json = jsonpath::select(&json_obj, "$..friends[0]").unwrap();

let ret = json!([
    {"name": "친구3", "age": 30},
    {"name": "친구1", "age": 20}
]);
assert_eq!(json, ret);

rust - jsonpath::select_as_str(json: &str, jsonpath: &str)

let ret = jsonpath::select_as_str(r#"
{
    "school": {
        "friends": [
                {"name": "친구1", "age": 20},
                {"name": "친구2", "age": 20}
            ]
    },
    "friends": [
        {"name": "친구3", "age": 30},
        {"name": "친구4"}
    ]
}
"#, "$..friends[0]").unwrap();

assert_eq!(ret, r#"[{"name":"친구3","age":30},{"name":"친구1","age":20}]"#);

rust - jsonpath::select_as<T: serde::de::DeserializeOwned>(json: &str, jsonpath: &str)

#[derive(Deserialize, PartialEq, Debug)]
struct Person {
    name: String,
    age: u8,
    phones: Vec<String>,
}

let ret: Person = jsonpath::select_as(r#"
{
    "person":
        {
            "name": "Doe John",
            "age": 44,
            "phones": [
                "+44 1234567",
                "+44 2345678"
            ]
        }
}
"#, "$.person").unwrap();

let person = Person {
    name: "Doe John".to_string(),
    age: 44,
    phones: vec!["+44 1234567".to_string(), "+44 2345678".to_string()],
};

assert_eq!(person, ret);

rust - jsonpath::compile(jsonpath: &str)

let mut template = jsonpath::compile("$..friends[0]");

let json_obj = json!({
    "school": {
        "friends": [
            {"name": "친구1", "age": 20},
            {"name": "친구2", "age": 20}
        ]
    },
    "friends": [
        {"name": "친구3", "age": 30},
        {"name": "친구4"}
]});

let json = template(&json_obj).unwrap();

let ret = json!([
    {"name": "친구3", "age": 30},
    {"name": "친구1", "age": 20}
]);

assert_eq!(json, ret);

rust - jsonpath::selector(json: &serde_json::value::Value)

let json_obj = json!({
    "school": {
        "friends": [
            {"name": "친구1", "age": 20},
            {"name": "친구2", "age": 20}
        ]
    },
    "friends": [
        {"name": "친구3", "age": 30},
        {"name": "친구4"}
]});

let mut selector = jsonpath::selector(&json_obj);

let json = selector("$..friends[0]").unwrap();

let ret = json!([
    {"name": "친구3", "age": 30},
    {"name": "친구1", "age": 20}
]);

assert_eq!(json, ret);

let json = selector("$..friends[1]").unwrap();

let ret = json!([
    {"name": "친구4"},
    {"name": "친구2", "age": 20}
]);

assert_eq!(json, ret);

rust - jsonpath::selector_as<T: serde::de::DeserializeOwned>(json: &serde_json::value::Value)

let json_obj = json!({
    "school": {
       "friends": [
            {"name": "친구1", "age": 20},
            {"name": "친구2", "age": 20}
        ]
    },
    "friends": [
        {"name": "친구3", "age": 30},
        {"name": "친구4"}
]});

#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Friend {
    name: String,
    age: Option<u8>,
}

let mut selector = jsonpath::selector_as::<Vec<Friend>>(&json_obj);

let json = selector("$..friends[0]").unwrap();

let ret = vec!(
    Friend { name: "친구3".to_string(), age: Some(30) },
    Friend { name: "친구1".to_string(), age: Some(20) }
);
assert_eq!(json, ret);

let json = selector("$..friends[1]").unwrap();

let ret = vec!(
    Friend { name: "친구4".to_string(), age: None },
    Friend { name: "친구2".to_string(), age: Some(20) }
);

assert_eq!(json, ret);