Skip to content

Latest commit

Β 

History

231 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

csonpath

That's not my path, that's not your path, but csonpath.

Project Sandbox

csonpath is a partial JSONPath implementation in C. It is backend-agnostic: the same core engine can query, update, and remove data from any value representation that supports array, object, and scalar semanticsβ€”not just JSON.

Out of the box it ships with C backends for json-c and yyjson, plus language bindings for Python and Rust.


πŸš€ Features

JSONPath Syntax

Feature Example Description
Dot notation $.a.b Access nested object fields
Bracket notation $['a']['b'] Alternative object/array access
Array index $.array[0] Access by zero-based index
Wildcard [*] $.array[*].field Iterate all array elements
Recursive descent .. $..name Search recursively for a key
Recursive descent wildcard ..* $..* Return all descendants
Union , (inside brackets) $['a','b'], $.array[0,1], $.items[?n==1, ?n==2] Match all listed selectors at once
OR fallback | $.a | $.b Try the left path first; fall back to the right one if it does not match. Only the first successful path is used.
Filters $.items[?price > 10] Filter array elements
Regex filters $.items[?name =~ "foo"] POSIX regex-based filtering
Multiple filters (&) $.items[?a=1 & b=2] Combine conditions
Subpath expressions $.obj[$.key] Use JSON values as dynamic path keys
@ current object $.items[?@.price > 10] Reference the current element in filters
Type selectors $..["@odata.id"]@string(), $.*@integer() Filter matches by JSON type (string, integer, null)

Operations

  • Find First β€” retrieve the first match.
  • Find All β€” retrieve all matches (returns an array).
  • Update or Create β€” modify existing values or create missing ones.
  • Remove β€” delete matching elements.
  • Callback β€” execute a custom callback on each match.
  • Update or Create Callback β€” traverse the path, creating missing intermediate objects and arrays, then invoke the callback on each leaf node.

🌐 Links


πŸ“„ Table of Contents


πŸ“¦ Installation

csonpath is a C library at its core. Pick the instructions for the language you want to use it from.

C (json-c)

Prerequisites: C compiler (gcc, clang, or tcc), json-c.

Just include the header in your project. There is no separate install step required:

#include "csonpath_json-c.h"

Make sure to link against json-c when compiling:

gcc myapp.c -o myapp $(pkg-config --cflags --libs json-c)

C (yyjson)

Prerequisites: C compiler (gcc, clang, or tcc), yyjson.

#include "csonpath_yyjson.h"

Link against yyjson when compiling:

gcc myapp.c -o myapp $(pkg-config --cflags --libs yyjson)

Python

Prerequisites: Python 3.x.

Install from PyPI:

pip install csonpath

To install from source (development):

pip install .
# or
make pip-dev

Rust

Prerequisites: Rust toolchain.

The Rust crate is located in rust/. Build and test it with:

cd rust
cargo build
cargo test

To depend on it from another Rust project:

[dependencies]
csonpath = { path = "rust" }

πŸ› οΈ Usage

C (json-c)

#include "csonpath_json-c.h"

static void my_cb(json_object *parent, struct csonpath_child_info *info,
                  json_object *current, void *ud)
{
    json_object_set_string(current, "modified");
}

int main(void)
{
    struct json_object *jobj = json_tokener_parse(json_str);
    struct csonpath *p = csonpath_new("$.a");

    /* Find First: return the first match, or NULL */
    struct json_object *ret = csonpath_find_first(p, jobj);

    /* Find All: return a NEW json_object array. Caller must free it. */
    ret = csonpath_find_all(p, jobj);
    json_object_put(ret);

    /* Remove: delete matching keys (or set array slots to null). Returns count. */
    int removed = csonpath_remove(p, jobj);

    /* Update or Create: replace matches, or create the full path if missing. */
    csonpath_update_or_create(p, jobj, json_object_new_string("new_value"));

    /* Callback: call a user function for every match. */
    csonpath_callback(p, jobj, my_cb, NULL);

    /* Update or Create Callback: like callback, but creates missing parents first,
       then invokes the callback on every leaf (existing or newly created). */
    csonpath_update_or_create_callback(p, jobj, my_cb, NULL);

    csonpath_destroy(p);
    json_object_put(jobj);
    return 0;
}

Python

import csonpath

data = {"a": "value", "array": [1, 2, 3]}
p = csonpath.CsonPath("$.a")

# Find First / Find All
p.find_first(data)   # -> "value"
p.find_all(data)     # -> ["value"]

# Remove: returns number of removed items
p.set_path("$.array[*]")
p.remove(data)

# Update or Create: builds missing objects/arrays automatically
p.set_path("$.x.y.z")
p.update_or_create(data, [])
# data is now {"a": "value", "array": [1, 2, 3], "x": {"y": {"z": []}}}

# Callback
p.set_path("$.a")
p.callback(data, lambda parent, idx, cur, _: parent.__setitem__(idx, cur.upper()))

# Update or Create Callback: creates parents, then calls cb on each leaf
p.set_path("$[*].a")
p.update_or_create_callback(dst, my_sync_fn, userdata)

πŸ“˜ C API Reference

The reference below uses the json-c backend as an example (struct json_object *). All backends expose the same functions with their own value type.

struct csonpath *csonpath_new(const char *path);

Create and initialize a new csonpath object.

int csonpath_set_path(struct csonpath *p, const char *path);

Change the path of an existing object.

int csonpath_compile(struct csonpath *p);

Compile the path expression. This is optionalβ€”paths are compiled automatically on first useβ€”but explicit compilation can help catch syntax errors earlier.

void csonpath_print_instruction(struct csonpath *p);

Print the compiled bytecode instructions (useful for debugging).

struct json_object *csonpath_find_first(struct csonpath *p, struct json_object *json);

Return the first matching value, or NULL if none is found.

struct json_object *csonpath_find_all(struct csonpath *p, struct json_object *json);

Return a new json_object array containing all matches. Must be freed with json_object_put().

int csonpath_remove(struct csonpath *p, struct json_object *json);

Remove all matching elements. Returns the number of elements removed.

int csonpath_update_or_create(struct csonpath *p, struct json_object *json, struct json_object *new_val);

Replace matching values with new_val, or create the path if it does not exist.

int csonpath_callback(struct csonpath *p, struct json_object *json,
                      json_c_callback callback, void *userdata);

Invoke callback for every match.

int csonpath_update_or_create_callback(struct csonpath *p, struct json_object *json,
                                       json_c_callback callback, void *userdata);

Like callback, but traverses the path while updating/creating missing intermediate objects.

void csonpath_destroy(struct csonpath *p);

Free the csonpath object.


πŸ“— Python API Reference

  • CsonPath(path, return_empty_array=False, jq_like=False) β€” Create a new csonpath object. Optional flags: return_empty_array returns [] instead of None when find_all() finds nothing; jq_like allows jq-style paths without a leading $.
  • set_path(path) β€” Change the path expression.
  • find_first(json) β€” Return the first match, or None.
  • find_all(json) β€” Return a list of all matches, or None (or [] if configured).
  • remove(json) β€” Remove all matches. Returns the number of removed items.
  • update_or_create(json, value) β€” Replace matches with value, or create the path.
  • callback(json, callback, callback_data=None) β€” Call callback(parent, idx, current, callback_data) for every match.
  • update_or_create_callback(json, callback, callback_data=None) β€” Same as callback, but creates missing parent objects along the path.

πŸ–₯️ CLI

A standalone C CLI is available in cli/csonpath_cli.c. It links directly against json-c and the csonpath C core, so it works without Python.

make csonpath          # build ./csonpath
make tests-cli         # run shell tests

It reads JSON from stdin, a file (-f), or a string (-s) and exposes the library operations through action flags.

# Get the first match (default)
echo '{"a": "value", "array": [1, 2, 3]}' | ./csonpath '$.a'
# => "value"

# Find all matches
echo '{"items": [{"price": 5}, {"price": 15}]}' | ./csonpath -a '$.items[?price > 10]'
# => [{"price":15}]

# One match per line
echo '{"array": [1, 2, 3]}' | ./csonpath -a -o lines '$.array[*]'
# => 1
# => 2
# => 3

# Set or create a value
echo '{"a": 1}' | ./csonpath --set '42' '$.x.y.z'
# => {"a":1,"x":{"y":{"z":42}}}

# Or with a positional value
echo '{"a": 1}' | ./csonpath '$.x.y.z' '42'

# Remove matches
echo '{"a": 1, "b": 2}' | ./csonpath -d '$.b'
# => {"a":1}

# Edit a file in place
./csonpath -f data.json -p -i --set '"2.0"' '$.version'

Options

Option Description
-a, --all Return all matches instead of the first one.
-d, --delete Remove matches and print the modified JSON.
--set VALUE Set PATH to VALUE (JSON).
VALUE (positional) Alternative to --set.
-r, --raw Treat the value as a raw string.
-i, --in-place Edit FILE in place (requires --file).
--strict Exit with an error if --delete removes nothing.
-f FILE, --file FILE Read JSON from FILE instead of stdin.
-s JSON, --string JSON Read JSON from a string.
-j, --jq-like Allow jq-style paths without a leading $.
-p, --pretty Pretty-print JSON output.
-o {json,pretty,raw,lines} Output format (default: json).
-e, --empty-array Return [] instead of nothing when -a matches nothing.

Exit codes

Code Meaning
0 Success.
1 No match found, or --strict delete found nothing.
EINVAL Usage error, JSON parse error, JSONPath compilation error, or invalid JSON value.
errno I/O error (e.g. ENOENT, EACCES).

🧩 Using Multiple Backends

You can include more than one csonpath backend in the same translation unit, provided you prefix all but one of them to avoid symbol collisions. Define CSONPATH_USE_PREFIX before a backend header to prefix its API with the backend name.

For example, to use both json-c and the immutable yyjson backend in the same file:

#define CSONPATH_USE_PREFIX
#include "csonpath_json-c.h"

#undef CSONPATH_USE_PREFIX
#include "csonpath_yyjson_const.h"

To use both yyjson backends (immutable and mutable) together, include the aggregator header:

#include "csonpath_yyjson.h"

When using the mutable yyjson backend, assign the document pointer to backend_ctx before mutable operations:

struct csonpath *mp = yyjson_mut_csonpath_new("$.a");
mp->backend_ctx = mdoc;
yyjson_mut_csonpath_update_or_create(mp, mroot, new_val);

πŸ”Œ Custom Backends

csonpath is designed to be backend-agnostic. The C core manipulates opaque value pointers through a small set of macros, so it can work with any data structure that supports array, object, and scalar semantics.

A backend is an adapter that implements those macros for a concrete value representation. To create a custom backend, define the required macros and types in a header file (similar to csonpath_json-c.h or csonpath_yyjson.h), then include your backend header before csonpath.h.

Existing backend adapters:

  • csonpath_json-c.h β€” json-c values
  • csonpath_yyjson.h β€” yyjson values
  • csonpath_python.c β€” Python C API values (used by the Python bindings)
  • rust/csonpath_rust_backend.h β€” Rust serde_json::Value (used by the Rust crate)

πŸ§ͺ Running Tests

C Tests

make tests-c

Rust Tests

cd rust
cargo test

Python Tests

make tests-py

All Tests

make tests

πŸ“ Directory Structure

File / Directory Description
csonpath.h, csonpath_do.h Core implementation (header-only style)
csonpath_json-c.h json-c backend
csonpath_yyjson.h yyjson backend
csonpath_python.c Python C extension backend
rust/ Rust crate and its C backend adapter
csonpath_my_fuzzing.h Fuzzer helpers (C)
tests/ C and Python test suites
bench/ Performance benchmarks

🀝 Contributing

We welcome contributions!

Please read our Contributing Guidelines and Code of Conduct before submitting a pull request.

Feel free to open issues or pull requests!


πŸ“œ License

BSD 3-Clause. See LICENSE.

About

Recode of jsonpath-ng

Topics

Resources

Code of conduct

Contributing

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages