That's not my path, that's not your path, but csonpath.
csonpath is a partial
implementation in C, with Python bindings. It allows you to query, update, and remove data from JSON objects using path expressions.
Unlike many JSONPath libraries, csonpath is backend-agnostic: it can work with any C library or environment that manipulates array, object, and scalar types—not just JSON. Out of the box, backends for
,
, Python and Rust (via serde_json) are provided.
———
🚀 Features
JSONPath Syntax
FeatureExampleDescriptionDot notation$.a.bAccess nested object fieldsBracket notation$['a']['b']Alternative object/array accessArray index$.array[0]Access by zero-based indexWildcard [*]$.array[*].fieldIterate all array elementsRecursive descent ..$..nameSearch recursively for a keyUnion , (inside brackets)$['a','b'], $.array[0,1], $.items[?n==1, ?n==2]Match all listed selectors at onceOR fallback |$.a | $.bTry 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 elementsRegex filters$.items[?name =~ "foo"]POSIX regex-based filteringMultiple filters (&)$.items[?a=1 & b=2]Combine conditionsSubpath expressions$.obj[$.key]Use JSON values as dynamic path keys@ current object$.items[[email protected] > 10]Reference the current element in filtersOperations
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
Project website:
https://github.com/outscale/csonpath
Join our community on
———
📄 Table of Contents
———
📦 Installation
Prerequisites
C compiler (gcc or clang)
library (for C usage)
Python 3.x (for Python bindings)
C (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 (Rust backend)
The Rust backend is located in rust/. It builds both a safe Rust API and the C glue required by the csonpath core.
cd rust cargo build cargo testTo use it from C, include the backend header and link against the produced static library:
#include"rust/csonpath_rust_backend.h"Python
Install from PyPI:
pip install csonpathTo install from source (development):
pip install .# or make pip-dev
———
🛠️ Usage
C (json-c)
#include"csonpath_json-c.h"staticvoidmy_cb(json_object*parent, structcsonpath_child_info*info, json_object*current, void*ud) { json_object_set_string(current, "modified"); } intmain(void) { structjson_object*jobj=json_tokener_parse(json_str); structcsonpath*p=csonpath_new("$.a"); /* Find First: return the first match, or NULL */structjson_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. */intremoved=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); return0; }Python
importcsonpathdata= {"a": "value", "array": [1, 2, 3]} p=csonpath.CsonPath("$.a") # Find First / Find Allp.find_first(data) # -> "value"p.find_all(data) # -> ["value"]# Remove: returns number of removed itemsp.set_path("$.array[*]") p.remove(data) # Update or Create: builds missing objects/arrays automaticallyp.set_path("$.x.y.z") p.update_or_create(data, []) # data is now {"a": "value", "array": [1, 2, 3], "x": {"y": {"z": []}}}# Callbackp.set_path("$.a") p.callback(data, lambdaparent, idx, cur, _: parent.__setitem__(idx, cur.upper())) # Update or Create Callback: creates parents, then calls cb on each leafp.set_path("$[*].a") p.update_or_create_callback(dst, my_sync_fn, userdata)
———
📘 C API Reference
structcsonpath*csonpath_new(constchar*path);Create and initialize a new csonpath object.
intcsonpath_set_path(structcsonpath*p, constchar*path);Change the path of an existing object.
intcsonpath_compile(structcsonpath*p);Compile the path expression. This is optional—paths are compiled automatically on first use—but explicit compilation can help catch syntax errors earlier.
voidcsonpath_print_instruction(structcsonpath*p);Print the compiled bytecode instructions (useful for debugging).
structjson_object*csonpath_find_first(structcsonpath*p, structjson_object*json);Return the first matching value, or NULL if none is found.
structjson_object*csonpath_find_all(structcsonpath*p, structjson_object*json);Return a new json_object array containing all matches. Must be freed with json_object_put().
intcsonpath_remove(structcsonpath*p, structjson_object*json);Remove all matching elements. Returns the number of elements removed.
intcsonpath_update_or_create(structcsonpath*p, structjson_object*json, structjson_object*new_val);Replace matching values with new_val, or create the path if it does not exist.
intcsonpath_callback(structcsonpath*p, structjson_object*json, json_c_callbackcallback, void*userdata);Invoke callback for every match.
intcsonpath_update_or_create_callback(structcsonpath*p, structjson_object*json, json_c_callbackcallback, void*userdata);Like callback, but traverses the path while updating/creating missing intermediate objects.
voidcsonpath_destroy(structcsonpath*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 testsIt 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 matchesecho'{"items": [{"price": 5}, {"price": 15}]}'| ./csonpath -a '$.items[?price > 10]'# => [{"price":15}]# One match per lineecho'{"array": [1, 2, 3]}'| ./csonpath -a -o lines '$.array[*]'# => 1# => 2# => 3# Set or create a valueecho'{"a": 1}'| ./csonpath --set '42''$.x.y.z'# => {"a":1,"x":{"y":{"z":42}}}# Or with a positional valueecho'{"a": 1}'| ./csonpath '$.x.y.z''42'# Remove matchesecho'{"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
OptionDescription-a, --allReturn all matches instead of the first one.-d, --deleteRemove matches and print the modified JSON.--set VALUESet PATH to VALUE (JSON).VALUE (positional)Alternative to --set.-r, --rawTreat the value as a raw string.-i, --in-placeEdit FILE in place (requires --file).--strictExit with an error if --delete removes nothing.-f FILE, --file FILERead JSON from FILE instead of stdin.-s JSON, --string JSONRead JSON from a string.-j, --jq-likeAllow jq-style paths without a leading $.-p, --prettyPretty-print JSON output.-o {json,pretty,raw,lines}Output format (default: json).-e, --empty-arrayReturn [] instead of nothing when -a matches nothing.Exit codes
CodeMeaning0Success.1No match found, or --strict delete found nothing.EINVALUsage error, JSON parse error, JSONPath compilation error, or invalid JSON value.errnoI/O error (e.g. ENOENT, EACCES).
———
🔌 Custom Backends
csonpath is designed to be backend-agnostic. It can work with any data structure that supports array, object, and scalar semantics.
To create a custom backend, define the required macros and types in a header file (similar to csonpath_json-c.h or csonpath_python.c), then include your backend header before csonpath.h. This allows you to adapt csonpath for manipulating data in any format that supports array/object semantics, giving you full flexibility beyond just JSON.
For more details, see the existing backend implementations:
csonpath_json-c.h — json-c backend
csonpath_yyjson.h — yyjson backend
csonpath_python.c — Python backend
rust/csonpath_rust_backend.h — Rust / serde_json backend
———
🧪 Running Tests
C Tests
make tests-cRust Tests
cd rust cargo testPython Tests
make tests-pyAll Tests
make tests
———
📁 Directory Structure
File / DirectoryDescriptioncsonpath.h, csonpath_do.hCore implementation (header-only style)csonpath_json-c.hjson-c backendcsonpath_yyjson.hyyjson backendcsonpath_python.cPython C extension backendrust/Rust backend (safe API, FFI, C glue)csonpath_my_fuzzing.hFuzzer helpers (C)tests/C and Python test suitesbench/Performance benchmarks
———
🤝 Contributing
We welcome contributions!
Please read our
and
before submitting a pull request.
Feel free to open issues or pull requests!
———
📜 License
BSD 3-Clause. See
.