jetro 0.1.0

Jetro is tool to transform, query and compare JSON format
Documentation

jetro

GitHub

Jetro is a library which provides a custom DSL for transforming, querying and comparing data in JSON format. It is easy to use and extend.

Jetro has minimal dependency, the traversal and eval algorithm is implemented on top of serde_json.

Jetro can be used inside Web Browser by compiling down to WASM. Visit Jetro Web to try it online, or clone it and give it a shot.

Jetro can be used in command line using Jetrocli.

Jetro combines access path with functions which operate on values matched within the pipeline. Access path uses / as separator similar to structure of URI, the start of access path should denote whether the access starts from root by using >, it is possible to traverse from root in nested paths by using <.

By convention, functions are denoted using # operator. Functions can be composed.

Function Action
#pick('string' | expression, ...) [ as | as* 'binding_value' ] Select a key from an object, bind it to a name, select multiple sub queries to create new object
#head Head of the list
#tail Tail of the list
#reverse Reverse the list
#all Whether all boolean values are true
#sum Sum of numbers
#formats('format with placeholder {} {}', 'key_a', 'key_b') [ -> | ->* 'binding_value' ] Insert formatted key:value into object or return it as single key:value
#filter('target_key' (>, <, >=, <=, ==, ~=, !=) (string, boolean, number)) Perform Filter on list
#map(x: x.y.z | x.y.z.some_method()) Map each item in a list with the given lambda
let data = serde_json::json!({
  "name": "mr snuggle",
  "some_entry": {
    "some_obj": {
      "obj": {
        "a": "object_a",
        "b": "object_b",
        "c": "object_c",
        "d": "object_d"
      }
    }
  }
});

let mut values = Path::collect(data, ">/..obj/#pick('a','b')");

#[derive(Serialize, Deserialize)]
struct Output {
   a: String,
   b: String,
}

let output: Option<Output> = values.from_index(0);

structure

Jetro consists of a parser, context wrapper which manages traversal and evaluation of each step of user input and a runtime for dynamic functions. The future version will support user-defined functions.

example

{
  "bar": {
    "meows": [
      10,
      20,
      30,
      40,
      50,
      60
    ],
    "person": {
      "firstname": "Mio",
      "lastname": "Snuggle",
      "hasFurr": true
    },
    "rolls": [
      {
        "roll": "on side"
      },
      {
        "roll": "in space"
      },
      {
        "roll": "in multiverse"
      },
      {
        "roll": "everywhere"
      }
    ]
  },
  "foo": [
    1,
    2,
    3,
    4,
    5,
    {
      "contract": {
        "kind": "Furry Purr",
        "hasFurr": false
      }
    }
  ],
  "friend": "Thunder Pur"
}

Queries

Get value associated with bar.

>/bar

result

"bar": {
  "meows": [
    10,
    20,
    30,
    40,
    50,
    60
  ],
  "person": {
    "firstname": "Mio",
    "lastname": "Snuggle"
  }
}

Get value associated with bar, return first value associated with any existing key.

>/bar/('person' | 'whatever')

result

{
  "firstname": "Mio",
  "hasFurr": true,
  "lastname": "Snuggle"
}

>/('foo' | 'bar' | 'non-exinsting-key')

result

[
  1,
  2,
  3,
  4,
  5,
  {
    "contract": {
      "kind": "Furry Purr"
    }
  }
]

>/('foo' | 'bar' | 'non-exinsting-key')/[:5]/#sum

result

15

>/..rolls/#filter('roll' == 'everywhere')

result

[
  {
    "roll": "everywhere"
  }
]

>/..rolls/#filter('priority' < 11 and 'roll' ~= 'ON Side')

result

[
  {
    "priority": 1,
    "roll": "on side"
  }
]

>/..rolls/#head/#formats('Rolling {}', 'roll') -> 'msg'

result

{
  "msg": "Rolling on side",
  "priority": 1,
  "roll": "on side"
}

>/..foo/[:4]

result

[
  1,
  2,
  3,
  4
]

>/..meows/[4:]

result

[
  50,
  60
]

>/#pick(>/..hasFurr/#all as 'totallyFurry')

result

{
  "totallyFurry": true
}


>/#pick('foo', >/..person/#formats('Herrn {} {}', 'firstname', 'lastname') ->* 'fullname')

result

{
  "foo": [
    1,
    2,
    3,
    4,
    5,
    {
      "contract": {
        "kind": "Furry Purr"
      }
    }
  ],
  "fullname": "Herrn Mio Snuggle"
}

>/..foo/..contract/#pick('kind' as 'contract', </..person/#formats('Welcome {}', 'firstname') ->* 'welcome_message')

result

{
  "contract": "Furry Purr",
  "welcome_message": "Welcome Mio"
}


>/..values/#map(x: x.name)/#head

result

"gearbox"