Skip to main content

Python bindings for jsonata-rs

Project description

jsonata-rs

crates.io docs.rs OpenSSF Scorecard

An (incomplete) implementation of JSONata in Rust.

Alpha version. All internal and external interfaces are considered unstable and subject to change without notice.

What is JSONata?

From the JSONata website:

  • Lightweight query and transformation language for JSON data
  • Inspired by the location path semantics of XPath 3.1
  • Sophisticated query expressions with minimal syntax
  • Built-in operators and functions for manipulating and combining data
  • Create user-defined functions
  • Format query results into any JSON output structure

Read the complete documentation, and try it out in Stedi's JSONata Playground.

Getting started

The API is not ergonomic (yet), as you must provide a bumpalo arena.

First, add the following to your Cargo.toml:

[dependencies]
jsonata-rs = "0"
bumpalo = "3.9.1"

Then you can evaluate an expression with JSON input like this:

use bumpalo::Bump;
use jsonata_rs::JsonAta;

// Create an arena for allocating values
let arena = Bump::new();

// Provide some JSON input. This could be read from a file or come from the network.
let input = r#"{ "name": "world" }";

// The JSONata expression to evaluate
let expr = r#""Hello, " & name & "!"";

// Parse the expression
let jsonata = JsonAta::new(expr, &arena).unwrap();

// Evaluate the expression against the input. The second parameter should
// contain any binding variables you want to be available during evaluations.
let result = jsonata.evaluate(Some(input), None).unwrap();

// Serialize the result into JSON
println!("{}", result.serialize(false));

There's also a basic CLI tool:

# cargo install jsonata-rs

# jsonata "1 + 1"
2

# jsonata '"Hello, " & name & "!"' '{ "name": "world" }'
"Hello, world!"

The expression and input can be specified on the command line, which requires manual escaping. Alternatively, they can be provided from files. Here's the --help output:

# jsonata --help
jsonata-rs
A command line JSON processor using JSONata

USAGE:
    jsonata [FLAGS] [OPTIONS] [ARGS]

FLAGS:
    -a, --ast        Parse the given expression, print the AST and exit
    -h, --help       Prints help information
    -V, --version    Prints version information

OPTIONS:
    -e, --expr-file <expr-file>      File containing the JSONata expression to evaluate (overrides expr on command line)
    -i, --input-file <input-file>    Input JSON file (if not specified, STDIN)

ARGS:
    <expr>     JSONata expression to evaluate
    <input>    JSON input

Python bindings (zopyx.pyjsonata)

The repository includes Python bindings built with PyO3 and maturin.

Build and install (local)

UV_CACHE_DIR=/tmp/uv-cache uv venv .venv --python 3.13 --clear
UV_CACHE_DIR=/tmp/uv-cache uv pip install --python .venv/bin/python maturin
UV_CACHE_DIR=/tmp/uv-cache .venv/bin/python -m maturin develop --features python

Usage

from zopyx.pyjsonata import evaluate, UNDEFINED, Jsonata

# Simple evaluation
print(evaluate("1 + 1"))  # 2

# With input data
data = {"name": "world"}
print(evaluate('"Hello, " & name & "!"', data))  # "Hello, world!"

# Optional bindings
bindings = {"x": 2, "y": 3}
print(evaluate("$x + $y", UNDEFINED, bindings))  # 5

# Reuse a compiled expression
expr = Jsonata("$sum([1,2,3])")
print(expr.evaluate())  # 6

Notes

  • UNDEFINED represents a missing input (distinct from None, which maps to JSON null).
  • Errors raise ValueError with a JSONata error code prefix (for example, T0410).

Missing (but planned) features

There are several JSONata features which are not yet implemented:

Differences from reference JSONata

Function signatures are not supported

Function signatures have problems as described here, and are not supported by this implementation.

Most of the JSONata functions, however, support being passed the context as the first argument as dictated by their signature, e.g:

["Hello", "world"].$substring(1, 2)

/* Output: ["el", "or"] */

This is implemented in each built-in function itself. For example, if $string sees that it is called without arguments, it will use the current context.

In addition, for all the built-in functions, type checking of arguments is also implemented directly in the functions themselves so that you get equivalent runtime errors for passing the wrong things to these functions as you would in reference JSONata.

Tests

Reference JSONata contains an extensive test suite with over 1,000 tests. Currently, this implementation passes almost 800 of these. You can run them like this:

cargo test testsuite

In tests/testsuite/groups are the tests groups that are passing, while tests/testsuite/skip contains the groups that still require feature implementation. There may be tests in the remaining groups that do pass, but I don't want to split them up - only when a test group fully passes is it moved.

Development status and goals

Status

There are several issues to be resolved:

  • There are obviously still a bunch of missing features. We're aiming for feature parity with the reference implementation wherever feasible.
  • The API has not had any real thought put into it yet.
  • This implementation attempts structural sharing of the input and output values with minimal heap allocations. This was a lot of effort working out the lifetimes that may not be worth it. We may consider removing Bumpalo in the future.
  • We have made a couple of optimization passes, but there are still lots of opportunities for improvement.
  • The code is spaghetti in some places and could be more Rust-idiomatic.

Goals

  • Feature-parity with the reference implementation (within reason)
  • Clean API and idiomatic code (i.e. make the easy things easy and the complex possible)
  • Well-documented for users and easy to onboard for contributors
  • Efficient and optimized with minimal low-hanging fruit.

Contribution

We welcome community contributions and pull requests.

License

This project is licensed under the Apache-2.0 License. Any code you submit will be released under that license.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

zopyx_pyjsonata-0.3.6-cp313-cp313-manylinux_2_28_x86_64.whl (799.4 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.28+ x86-64

zopyx_pyjsonata-0.3.6-cp312-cp312-manylinux_2_28_x86_64.whl (799.8 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.28+ x86-64

zopyx_pyjsonata-0.3.6-cp311-cp311-manylinux_2_28_x86_64.whl (800.9 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.28+ x86-64

File details

Details for the file zopyx_pyjsonata-0.3.6-cp313-cp313-manylinux_2_28_x86_64.whl.

File metadata

  • Download URL: zopyx_pyjsonata-0.3.6-cp313-cp313-manylinux_2_28_x86_64.whl
  • Upload date:
  • Size: 799.4 kB
  • Tags: CPython 3.13, manylinux: glibc 2.28+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for zopyx_pyjsonata-0.3.6-cp313-cp313-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 37a208cba3f2afeb4ae6d233525b7a252a00d5d0a0c5b79c32e3582320530def
MD5 9cbc569099bd04f23f0780ca860c422a
BLAKE2b-256 066de70e8aea77d98ff4d79ad09cbf99632a1a5eae039dd4db65d66cb0a4dd47

See more details on using hashes here.

File details

Details for the file zopyx_pyjsonata-0.3.6-cp312-cp312-manylinux_2_28_x86_64.whl.

File metadata

  • Download URL: zopyx_pyjsonata-0.3.6-cp312-cp312-manylinux_2_28_x86_64.whl
  • Upload date:
  • Size: 799.8 kB
  • Tags: CPython 3.12, manylinux: glibc 2.28+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for zopyx_pyjsonata-0.3.6-cp312-cp312-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 a067f62938ccc240b038a72a3c536d9aafa8d491f62c463e23fc89e133a75cad
MD5 78885227f2cc67c0d9b3bb9bd1bb349b
BLAKE2b-256 0f156889da6f43b3fa1fa02215d580506ccb4052edeac53a034d276a6aac1f46

See more details on using hashes here.

File details

Details for the file zopyx_pyjsonata-0.3.6-cp311-cp311-manylinux_2_28_x86_64.whl.

File metadata

  • Download URL: zopyx_pyjsonata-0.3.6-cp311-cp311-manylinux_2_28_x86_64.whl
  • Upload date:
  • Size: 800.9 kB
  • Tags: CPython 3.11, manylinux: glibc 2.28+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for zopyx_pyjsonata-0.3.6-cp311-cp311-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 b211202a30c02c07cc8f2fe5a02a7d1b671a922388e2ea112ea97b0d3ec45b4d
MD5 070e1167ad2abfc7cdcaf490728b9c05
BLAKE2b-256 034a2f4b5cb56441a7b8a8437e1b17fab29d2d3d8c608badcba5b85a7141595f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page