jsonpath_lib 0.1.2

JsonPath in Rust and Webassembly - Webassembly Demo: https://freestrings.github.io/jsonpath
Documentation

jsonpath-lib

Build Status

Rust 버전 JsonPath 구현이다. Rust 구현과 동일한 기능을 Webassembly 로 제공하는 것도 목표.

The Rust version is a JsonPath implementation. It is also aimed to provide the same functionality as Webassembly in Rust implementation.

왜?

To enjoy Rust!

목차

With Javascript (Webassembly)

With Rust (as library)

With AWS API Gateway

Benchmark

With Javascript (WebAssembly)

jsonpath-wasm library

(not yet published jsonpath-wasm)

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

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

let jsonObj = {
   "school": {
       "friends": [{"id": 0}, {"id": 1}]
   },
   "friends": [{"id": 0}, {"id": 1}]
};
let ret = [{"id": 0}, {"id": 0}];

let a = jsonpath.read(JSON.stringify(jsonObj), "$..friends[0]");
let b = jsonpath.read(jsonObj, "$..friends[0]");
console.log(
    JSON.stringify(ret) == JSON.stringify(a),
    JSON.stringify(a) == JSON.stringify(b)
);

javascript - jsonpath.compile(jsonpath: string)

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

let jsonObj = {
    "school": {
        "friends": [ {"id": 0}, {"id": 1} ]
    },
    "friends": [ {"id": 0}, {"id": 1} ]
};

let ret = JSON.stringify([ {"id": 0}, {"id": 0} ]);

// 1. read as json object
console.log(JSON.stringify(template(jsonObj)) == ret);
// 2. read as json string
console.log(JSON.stringify(template(JSON.stringify(jsonObj))) == ret);

let jsonObj2 = {
    "school": {
        "friends": [ 
            {"name": "Millicent Norman"}, 
            {"name": "Vincent Cannon"} 
        ]
    },
    "friends": [ {"id": 0}, {"id": 1} ]
};

let ret2 = JSON.stringify([ {"id": 0}, {"name": "Millicent Norman"} ]);

// 1. read as json object
console.log(JSON.stringify(template(jsonObj2)) == ret2);
// 2. read as json string
console.log(JSON.stringify(template(JSON.stringify(jsonObj2))) == ret2);

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

let jsonObj = {
    "school": {
        "friends": [{"id": 0}, {"id": 1}]
    },
    "friends": [{"id": 0},{"id": 1}]
};

let ret1 = JSON.stringify([ {"id": 0}, {"id": 0} ]);
let ret2 = JSON.stringify([ {"id": 1}, {"id": 1} ]);

// 1. read as json object
let reader = jsonpath.reader(jsonObj);
console.log(JSON.stringify(reader("$..friends[0]")) == ret1);
console.log(JSON.stringify(reader("$..friends[1]")) == ret2);

// 2. read as json string
let reader2 = jsonpath.reader(JSON.stringify(jsonObj));
console.log(JSON.stringify(reader2("$..friends[0]")) == ret1);
console.log(JSON.stringify(reader2("$..friends[1]")) == ret2);

javascript - examples

Demo: https://freestrings.github.io/jsonpath/

json 데이터 (참고 사이트: https://github.com/json-path/JsonPath)

{
    "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
}
JsonPath (click link to try) Result
$.store.book[*].author The authors of all books
$..author All authors
$.store.* All things, both books and bicycles
$.store..price The price of everything
$..book[2] The third book
$..book[-2] The second to last book
$..book[0,1] The first two books
$..book[:2] All books from index 0 (inclusive) until index 2 (exclusive)
$..book[1:2] All books from index 1 (inclusive) until index 2 (exclusive)
$..book[-2:] Last two books
$..book[2:] Book number two from tail
$..book[?(@.isbn)] All books with an ISBN number
$.store.book[?(@.price < 10)] All books in store cheaper than 10
$..* Give me every thing
$..book[ ?((@.price == 12.99 | | $.store.bicycle.price < @.price) || @.category == "reference")] Complex filter

With Rust (as library)

jsonpath_lib library

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

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

let json_obj = json!({
    "school": {
        "friends": [{"id": 0}, {"id": 1}]
    },
    "friends": [{"id": 0}, {"id": 1}]
});
let json = jsonpath::read(json_obj, "$..friends[0]").unwrap();
let ret = json!([ {"id": 0}, {"id": 0} ]);
assert_eq!(json, ret)

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

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

let json_obj = json!({
    "school": {
        "friends": [ {"id": 0}, {"id": 1} ]
    },
    "friends": [ {"id": 0}, {"id": 1} ]
});

let json = template(json_obj).unwrap();
let ret = json!([ {"id": 0}, {"id": 0} ]);
assert_eq!(json, ret);

let json_obj = json!({
    "school": {
        "friends": [ {"name": "Millicent Norman"}, {"name": "Vincent Cannon"} ]
    },
    "friends": [ {"id": 0}, {"id": 1} ]
});

let json = template(json_obj).unwrap();
let ret = json!([ {"id": 0}, {"name": "Millicent Norman"} ]);
assert_eq!(json, ret);

rust - jsonpath::reader(json: serde_json::value::Value)

let json_obj = json!({
    "school": {
        "friends": [{"id": 0}, {"id": 1}]
    },
    "friends": [{"id": 0},{"id": 1}]
});

let mut reader = jsonpath::reader(json_obj);

let json = reader("$..friends[0]").unwrap();
let ret = json!([ {"id": 0}, {"id": 0} ]);
assert_eq!(json, ret);

let json = reader("$..friends[1]").unwrap();
let ret = json!([ {"id": 1}, {"id": 1} ]);
assert_eq!(json, ret);

rust - examples

let json_obj = 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 mut reader = jsonpath::reader(json_obj);

$.store.book[*].author

let json = reader("$.store.book[*].author").unwrap();
let ret = json!([
  "Nigel Rees",
  "Evelyn Waugh",
  "Herman Melville",
  "J. R. R. Tolkien"
]);
assert_eq!(json, ret);

$..author

let json = reader("$..author").unwrap();
let ret = json!([
  "Nigel Rees",
  "Evelyn Waugh",
  "Herman Melville",
  "J. R. R. Tolkien"
]);
assert_eq!(json, ret);

$.store.*

let json = reader("$.store.*").unwrap();
let ret = json!([
    [
        {
          "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
    }
]);
assert_eq!(ret, json);

$.store..price

let json = reader("$.store..price").unwrap();
let ret = json!([8.95, 12.99, 8.99, 22.99, 19.95]);
assert_eq!(ret, json);

$..book[2]

let json = reader("$..book[2]").unwrap();
let ret = json!([{
    "category" : "fiction",
    "author" : "Herman Melville",
    "title" : "Moby Dick",
    "isbn" : "0-553-21311-3",
    "price" : 8.99
}]);
assert_eq!(ret, json);

$..book[-2]

let json = reader("$..book[-2]").unwrap();
let ret = json!([{
    "category" : "fiction",
    "author" : "Herman Melville",
    "title" : "Moby Dick",
    "isbn" : "0-553-21311-3",
    "price" : 8.99
 }]);
assert_eq!(ret, json);

$..book[0,1]

let json = reader("$..book[0,1]").unwrap();
let ret = json!([
  {
    "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
  }
]);
assert_eq!(ret, json);

$..book[:2]

let json = reader("$..book[:2]").unwrap();
let ret = json!([
  {
    "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
  }
]);
assert_eq!(ret, json);

$..book[2:]

let json = reader("$..book[2:]").unwrap();
let ret = json!([
  {
    "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
  }
]);
assert_eq!(ret, json);

$..book[?(@.isbn)]

let json = reader("$..book[?(@.isbn)]").unwrap();
let ret = json!([
  {
    "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
  }
]);
assert_eq!(ret, json);

$.store.book[?(@.price < 10)]

let json = reader("$.store.book[?(@.price < 10)]").unwrap();
let ret = json!([
  {
    "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
  }
]);
assert_eq!(ret, json);

$..book[?((@.price == 12.99 || $.store.bicycle.price < @.price) || @.category == "reference")]

let json = reader(r#"$..book[
                    ?(
                        (@.price == 12.99 || $.store.bicycle.price < @.price) 
                        || @.category == "reference"
                     )]"#).unwrap();
let ret = json!([
  {
    "category": "fiction",
    "author": "Evelyn Waugh",
    "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
  }
]);
assert_eq!(ret, json);

With AWS API Gateway

Simple time check ( compare with dchester/jsonpath)

jsonpath is dchester/jsonpath jsonpath-wasm is freestrings/jsonpath's compiled to webassembly

jsonpath-wasm is slow performance on Chrome browser and in NodeJS. not yet usable. :)

Browser Bench Demo

Chrome: 72.0

Something to wrong in chrome

jsonpath, 134
jsonpath-wasm- reader, 1409
jsonpath-wasm- compile, 3237
jsonpath-wasm- read, 5302

Firefox: 65.0

jsonpath-wasm is faster than jsonpath

jsonpath, 301
jsonpath-wasm- reader, 166
jsonpath-wasm- compile, 130
jsonpath-wasm- read, 144

NodeJs

  • NodeJS: 11.0
  • CPU: Intel Core i5-4460
  • Memory: 16GB

Rust > jsonpath > jsonpath-wasm

cd benches && ./bench_node_vs_rust.sh

$..book[?(@.price<30 && @.category==fiction)] (loop 100,000)

Rust: 

real	0m1.141s
user	0m1.137s
sys	0m0.004s

NodeJs - jsonpath module: 

real	0m3.718s
user	0m4.175s
sys	0m0.050s

NodeJs - jsonpath-wasm module: 

real	0m10.205s
user	0m10.281s
sys	0m0.383s