csonpath 0.14.0__tar.gz → 0.16.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. {csonpath-0.14.0 → csonpath-0.16.0}/Makefile +23 -3
  2. csonpath-0.16.0/PKG-INFO +310 -0
  3. csonpath-0.16.0/README.md +301 -0
  4. {csonpath-0.14.0 → csonpath-0.16.0}/configure +5 -0
  5. csonpath-0.16.0/csonpath.egg-info/PKG-INFO +310 -0
  6. {csonpath-0.14.0 → csonpath-0.16.0}/csonpath.h +308 -120
  7. {csonpath-0.14.0 → csonpath-0.16.0}/csonpath_do.h +67 -17
  8. {csonpath-0.14.0 → csonpath-0.16.0}/csonpath_json-c.h +11 -9
  9. {csonpath-0.14.0 → csonpath-0.16.0}/csonpath_python.c +69 -33
  10. {csonpath-0.14.0 → csonpath-0.16.0}/csonpath_yyjson.h +20 -17
  11. {csonpath-0.14.0 → csonpath-0.16.0}/pyproject.toml +2 -2
  12. csonpath-0.16.0/setup.py +64 -0
  13. csonpath-0.14.0/PKG-INFO +0 -183
  14. csonpath-0.14.0/README.md +0 -174
  15. csonpath-0.14.0/csonpath.egg-info/PKG-INFO +0 -183
  16. csonpath-0.14.0/setup.py +0 -35
  17. {csonpath-0.14.0 → csonpath-0.16.0}/LICENSE +0 -0
  18. {csonpath-0.14.0 → csonpath-0.16.0}/MANIFEST.in +0 -0
  19. {csonpath-0.14.0 → csonpath-0.16.0}/csonpath.egg-info/SOURCES.txt +0 -0
  20. {csonpath-0.14.0 → csonpath-0.16.0}/csonpath.egg-info/dependency_links.txt +0 -0
  21. {csonpath-0.14.0 → csonpath-0.16.0}/csonpath.egg-info/requires.txt +0 -0
  22. {csonpath-0.14.0 → csonpath-0.16.0}/csonpath.egg-info/top_level.txt +0 -0
  23. {csonpath-0.14.0 → csonpath-0.16.0}/setup.cfg +0 -0
  24. {csonpath-0.14.0 → csonpath-0.16.0}/tiny-regex-c/.git +0 -0
  25. {csonpath-0.14.0 → csonpath-0.16.0}/tiny-regex-c/.github/workflows/c-cpp.yml +0 -0
  26. {csonpath-0.14.0 → csonpath-0.16.0}/tiny-regex-c/.gitignore +0 -0
  27. {csonpath-0.14.0 → csonpath-0.16.0}/tiny-regex-c/LICENSE +0 -0
  28. {csonpath-0.14.0 → csonpath-0.16.0}/tiny-regex-c/Makefile +0 -0
  29. {csonpath-0.14.0 → csonpath-0.16.0}/tiny-regex-c/README.md +0 -0
  30. {csonpath-0.14.0 → csonpath-0.16.0}/tiny-regex-c/clib.json +0 -0
  31. {csonpath-0.14.0 → csonpath-0.16.0}/tiny-regex-c/formal_verification.md +0 -0
  32. {csonpath-0.14.0 → csonpath-0.16.0}/tiny-regex-c/re.c +0 -0
  33. {csonpath-0.14.0 → csonpath-0.16.0}/tiny-regex-c/re.h +0 -0
  34. {csonpath-0.14.0 → csonpath-0.16.0}/tiny-regex-c/scripts/exrex.py +0 -0
  35. {csonpath-0.14.0 → csonpath-0.16.0}/tiny-regex-c/scripts/regex_test.py +0 -0
  36. {csonpath-0.14.0 → csonpath-0.16.0}/tiny-regex-c/scripts/regex_test_neg.py +0 -0
  37. {csonpath-0.14.0 → csonpath-0.16.0}/tiny-regex-c/tests/test1.c +0 -0
  38. {csonpath-0.14.0 → csonpath-0.16.0}/tiny-regex-c/tests/test2.c +0 -0
  39. {csonpath-0.14.0 → csonpath-0.16.0}/tiny-regex-c/tests/test3.c +0 -0
  40. {csonpath-0.14.0 → csonpath-0.16.0}/tiny-regex-c/tests/test_compile.c +0 -0
  41. {csonpath-0.14.0 → csonpath-0.16.0}/tiny-regex-c/tests/test_print.c +0 -0
  42. {csonpath-0.14.0 → csonpath-0.16.0}/tiny-regex-c/tests/test_rand.c +0 -0
  43. {csonpath-0.14.0 → csonpath-0.16.0}/tiny-regex-c/tests/test_rand_neg.c +0 -0
  44. {csonpath-0.14.0 → csonpath-0.16.0}/tiny-regex-c/tests/test_thread.c +0 -0
@@ -1,9 +1,13 @@
1
1
  JSON_C_CFLAGS=$(shell pkg-config --cflags json-c)
2
2
  JSON_C_LDFLAGS=$(shell pkg-config --libs json-c)
3
+ YYJSON_CFLAGS=$(shell pkg-config --cflags yyjson)
4
+ YYJSON_LDFLAGS=$(shell pkg-config --libs yyjson)
3
5
 
4
6
  include config.mk
5
7
 
6
- all: test-json-c-get-a test-json-update test-json-filter test-json-subpath test-json-c-array-root test-json-filter-and-missing-key test-json-get-array-big-index
8
+ all: test-json-c-get-a test-json-update test-json-filter test-json-subpath test-json-c-array-root test-json-filter-and-missing-key test-json-get-array-big-index test-json-union test-json-audit-bugs test-json-crash-vectors
9
+
10
+ YYJSON_TESTS=test-yyjson
7
11
 
8
12
  bench:
9
13
  make -C bench
@@ -36,7 +40,19 @@ test-json-filter-and-missing-key: tests/json-c/filter-and-missing-key.c csonpath
36
40
  test-json-get-array-big-index: tests/json-c/get-array-big-index.c csonpath_json-c.h csonpath.h csonpath_do.h
37
41
  $(CC) tests/json-c/get-array-big-index.c $(EXTRA_FILES) $(JSON_C_CFLAGS) $(CFLAGS) -Wno-format -I./ -o test-json-get-array-big-index $(JSON_C_LDFLAGS) $(LDFLAGS)
38
42
 
39
- tests-c: test-json-c-get-a test-json-update test-json-filter test-json-subpath test-json-c-array-root test-json-filter-and-missing-key test-json-get-array-big-index
43
+ test-json-union: tests/json-c/union.c csonpath_json-c.h csonpath.h csonpath_do.h
44
+ $(CC) tests/json-c/union.c $(EXTRA_FILES) $(JSON_C_CFLAGS) $(CFLAGS) -Wno-format -I./ -o test-json-union $(JSON_C_LDFLAGS) $(LDFLAGS)
45
+
46
+ test-json-audit-bugs: tests/json-c/audit-bugs.c csonpath_json-c.h csonpath.h csonpath_do.h
47
+ $(CC) tests/json-c/audit-bugs.c $(EXTRA_FILES) $(JSON_C_CFLAGS) $(CFLAGS) -Wno-format -I./ -o test-json-audit-bugs $(JSON_C_LDFLAGS) $(LDFLAGS)
48
+
49
+ test-json-crash-vectors: tests/json-c/crash-vectors.c csonpath_json-c.h csonpath.h csonpath_do.h
50
+ $(CC) tests/json-c/crash-vectors.c $(EXTRA_FILES) $(JSON_C_CFLAGS) $(CFLAGS) -Wno-format -I./ -o test-json-crash-vectors $(JSON_C_LDFLAGS) $(LDFLAGS)
51
+
52
+ test-yyjson: tests/yyjson/test-yyjson.c csonpath_yyjson.h csonpath.h csonpath_do.h
53
+ $(CC) tests/yyjson/test-yyjson.c $(EXTRA_FILES) $(YYJSON_CFLAGS) $(CFLAGS) -Wno-format -I./ -o test-yyjson $(YYJSON_LDFLAGS) $(LDFLAGS)
54
+
55
+ tests-c: test-json-c-get-a test-json-update test-json-filter test-json-subpath test-json-c-array-root test-json-filter-and-missing-key test-json-get-array-big-index test-json-union test-json-audit-bugs test-json-crash-vectors test-yyjson
40
56
  ./test-json-c-get-a
41
57
  ./test-json-update
42
58
  ./test-json-filter
@@ -44,6 +60,10 @@ tests-c: test-json-c-get-a test-json-update test-json-filter test-json-subpath t
44
60
  ./test-json-c-array-root
45
61
  ./test-json-filter-and-missing-key
46
62
  ./test-json-get-array-big-index
63
+ ./test-json-union
64
+ ./test-json-audit-bugs
65
+ ./test-json-crash-vectors
66
+ ./test-yyjson
47
67
 
48
68
  pip-dev:
49
69
  pip install -e .[dev] --force-reinstall
@@ -54,5 +74,5 @@ tests-py: pip-dev
54
74
  tests: tests-py tests-c
55
75
 
56
76
  clean:
57
- rm -rvf test-json-c-get-a test-json-update test-json-filter test-json-subpath test-json-c-array-root test-json-filter-and-missing-key test-json-get-array-big-index
77
+ rm -rvf test-json-c-get-a test-json-update test-json-filter test-json-subpath test-json-c-array-root test-json-filter-and-missing-key test-json-get-array-big-index test-json-union test-json-audit-bugs test-json-crash-vectors test-yyjson
58
78
 
@@ -0,0 +1,310 @@
1
+ Metadata-Version: 2.4
2
+ Name: csonpath
3
+ Version: 0.16.0
4
+ Description-Content-Type: text/markdown
5
+ License-File: LICENSE
6
+ Provides-Extra: dev
7
+ Requires-Dist: pytest>=8.3.5; extra == "dev"
8
+ Dynamic: license-file
9
+
10
+ # csonpath
11
+
12
+ > That's not my path, that's not your path, but **csonpath**.
13
+
14
+ [![Project Sandbox](https://docs.outscale.com/fr/userguide/_images/Project-Sandbox-yellow.svg)](https://docs.outscale.com/en/userguide/Open-Source-Projects.html)
15
+
16
+ **csonpath** is a partial [JSONPath](https://goessner.net/articles/JsonPath/) implementation in C, with Python bindings. It allows you to query, update, and remove data from JSON objects using path expressions.
17
+
18
+ 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 [json-c](https://github.com/json-c/json-c) and Python are provided.
19
+
20
+ ---
21
+
22
+ ## 🚀 Features
23
+
24
+ ### JSONPath Syntax
25
+
26
+ | Feature | Example | Description |
27
+ |---|---|---|
28
+ | Dot notation | `$.a.b` | Access nested object fields |
29
+ | Bracket notation | `$['a']['b']` | Alternative object/array access |
30
+ | Array index | `$.array[0]` | Access by zero-based index |
31
+ | Wildcard `[*]` | `$.array[*].field` | Iterate all array elements |
32
+ | Recursive descent `..` | `$..name` | Search recursively for a key |
33
+ | Union `,` (inside brackets) | `$['a','b']`, `$.array[0,1]`, `$.items[?n==1, ?n==2]` | Match all listed selectors at once |
34
+ | 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. |
35
+ | Filters | `$.items[?price > 10]` | Filter array elements |
36
+ | Regex filters | `$.items[?name =~ "foo"]` | POSIX regex-based filtering |
37
+ | Multiple filters (`&`) | `$.items[?a=1 & b=2]` | Combine conditions |
38
+ | Subpath expressions | `$.obj[$.key]` | Use JSON values as dynamic path keys |
39
+ | `@` current object | `$.items[?@.price > 10]` | Reference the current element in filters |
40
+
41
+ ### Operations
42
+ - **Find First** — retrieve the first match.
43
+ - **Find All** — retrieve all matches (returns an array).
44
+ - **Update or Create** — modify existing values or create missing ones.
45
+ - **Remove** — delete matching elements.
46
+ - **Callback** — execute a custom callback on each match.
47
+ - **Update or Create Callback** — traverse the path, creating missing intermediate objects and arrays, then invoke the callback on each leaf node.
48
+
49
+ ---
50
+
51
+ ## 🌐 Links
52
+
53
+ - Project website: https://github.com/outscale/csonpath
54
+ - Join our community on [Discord](https://discord.gg/HUVtY5gT6s)
55
+
56
+ ---
57
+
58
+ ## 📄 Table of Contents
59
+
60
+ - [Features](#-features)
61
+ - [Installation](#-installation)
62
+ - [Usage](#-usage)
63
+ - [C API Reference](#-c-api-reference)
64
+ - [Python API Reference](#-python-api-reference)
65
+ - [Custom Backends](#-custom-backends)
66
+ - [Running Tests](#-running-tests)
67
+ - [Directory Structure](#-directory-structure)
68
+ - [Contributing](#-contributing)
69
+ - [License](#-license)
70
+
71
+ ---
72
+
73
+ ## 📦 Installation
74
+
75
+ ### Prerequisites
76
+ - C compiler (`gcc` or `clang`)
77
+ - [json-c](https://github.com/json-c/json-c) library (for C usage)
78
+ - Python 3.x (for Python bindings)
79
+
80
+ ### C (json-c)
81
+ Just include the header in your project. There is no separate install step required:
82
+ ```c
83
+ #include "csonpath_json-c.h"
84
+ ```
85
+ Make sure to link against `json-c` when compiling:
86
+ ```sh
87
+ gcc myapp.c -o myapp $(pkg-config --cflags --libs json-c)
88
+ ```
89
+
90
+ ### Python
91
+ Install from PyPI:
92
+ ```sh
93
+ pip install csonpath
94
+ ```
95
+
96
+ To install from source (development):
97
+ ```sh
98
+ pip install .
99
+ # or
100
+ make pip-dev
101
+ ```
102
+
103
+ ---
104
+
105
+ ## 🛠️ Usage
106
+
107
+ ### C (json-c)
108
+
109
+ ```c
110
+ #include "csonpath_json-c.h"
111
+
112
+ static void my_cb(json_object *parent, struct csonpath_child_info *info,
113
+ json_object *current, void *ud)
114
+ {
115
+ json_object_set_string(current, "modified");
116
+ }
117
+
118
+ int main(void)
119
+ {
120
+ struct json_object *jobj = json_tokener_parse(json_str);
121
+ struct csonpath *p = csonpath_new("$.a");
122
+
123
+ /* Find First: return the first match, or NULL */
124
+ struct json_object *ret = csonpath_find_first(p, jobj);
125
+
126
+ /* Find All: return a NEW json_object array. Caller must free it. */
127
+ ret = csonpath_find_all(p, jobj);
128
+ json_object_put(ret);
129
+
130
+ /* Remove: delete matching keys (or set array slots to null). Returns count. */
131
+ int removed = csonpath_remove(p, jobj);
132
+
133
+ /* Update or Create: replace matches, or create the full path if missing. */
134
+ csonpath_update_or_create(p, jobj, json_object_new_string("new_value"));
135
+
136
+ /* Callback: call a user function for every match. */
137
+ csonpath_callback(p, jobj, my_cb, NULL);
138
+
139
+ /* Update or Create Callback: like callback, but creates missing parents first,
140
+ then invokes the callback on every leaf (existing or newly created). */
141
+ csonpath_update_or_create_callback(p, jobj, my_cb, NULL);
142
+
143
+ csonpath_destroy(p);
144
+ json_object_put(jobj);
145
+ return 0;
146
+ }
147
+ ```
148
+
149
+ ### Python
150
+
151
+ ```python
152
+ import csonpath
153
+
154
+ data = {"a": "value", "array": [1, 2, 3]}
155
+ p = csonpath.CsonPath("$.a")
156
+
157
+ # Find First / Find All
158
+ p.find_first(data) # -> "value"
159
+ p.find_all(data) # -> ["value"]
160
+
161
+ # Remove: returns number of removed items
162
+ p.set_path("$.array[*]")
163
+ p.remove(data)
164
+
165
+ # Update or Create: builds missing objects/arrays automatically
166
+ p.set_path("$.x.y.z")
167
+ p.update_or_create(data, [])
168
+ # data is now {"a": "value", "array": [1, 2, 3], "x": {"y": {"z": []}}}
169
+
170
+ # Callback
171
+ p.set_path("$.a")
172
+ p.callback(data, lambda parent, idx, cur, _: parent.__setitem__(idx, cur.upper()))
173
+
174
+ # Update or Create Callback: creates parents, then calls cb on each leaf
175
+ p.set_path("$[*].a")
176
+ p.update_or_create_callback(dst, my_sync_fn, userdata)
177
+ ```
178
+
179
+ ---
180
+
181
+ ## 📘 C API Reference
182
+
183
+ ```c
184
+ struct csonpath *csonpath_new(const char *path);
185
+ ```
186
+ Create and initialize a new csonpath object.
187
+
188
+ ```c
189
+ int csonpath_set_path(struct csonpath *p, const char *path);
190
+ ```
191
+ Change the path of an existing object.
192
+
193
+ ```c
194
+ int csonpath_compile(struct csonpath *p);
195
+ ```
196
+ Compile the path expression. This is optional—paths are compiled automatically on first use—but explicit compilation can help catch syntax errors earlier.
197
+
198
+ ```c
199
+ void csonpath_print_instruction(struct csonpath *p);
200
+ ```
201
+ Print the compiled bytecode instructions (useful for debugging).
202
+
203
+ ```c
204
+ struct json_object *csonpath_find_first(struct csonpath *p, struct json_object *json);
205
+ ```
206
+ Return the first matching value, or `NULL` if none is found.
207
+
208
+ ```c
209
+ struct json_object *csonpath_find_all(struct csonpath *p, struct json_object *json);
210
+ ```
211
+ Return a new `json_object` array containing all matches. **Must be freed with `json_object_put()`.**
212
+
213
+ ```c
214
+ int csonpath_remove(struct csonpath *p, struct json_object *json);
215
+ ```
216
+ Remove all matching elements. Returns the number of elements removed.
217
+
218
+ ```c
219
+ int csonpath_update_or_create(struct csonpath *p, struct json_object *json, struct json_object *new_val);
220
+ ```
221
+ Replace matching values with `new_val`, or create the path if it does not exist.
222
+
223
+ ```c
224
+ int csonpath_callback(struct csonpath *p, struct json_object *json,
225
+ json_c_callback callback, void *userdata);
226
+ ```
227
+ Invoke `callback` for every match.
228
+
229
+ ```c
230
+ int csonpath_update_or_create_callback(struct csonpath *p, struct json_object *json,
231
+ json_c_callback callback, void *userdata);
232
+ ```
233
+ Like `callback`, but traverses the path while updating/creating missing intermediate objects.
234
+
235
+ ```c
236
+ void csonpath_destroy(struct csonpath *p);
237
+ ```
238
+ Free the csonpath object.
239
+
240
+ ---
241
+
242
+ ## 📗 Python API Reference
243
+
244
+ - **`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 `$`.
245
+ - **`set_path(path)`** — Change the path expression.
246
+ - **`find_first(json)`** — Return the first match, or `None`.
247
+ - **`find_all(json)`** — Return a list of all matches, or `None` (or `[]` if configured).
248
+ - **`remove(json)`** — Remove all matches. Returns the number of removed items.
249
+ - **`update_or_create(json, value)`** — Replace matches with `value`, or create the path.
250
+ - **`callback(json, callback, callback_data=None)`** — Call `callback(parent, idx, current, callback_data)` for every match.
251
+ - **`update_or_create_callback(json, callback, callback_data=None)`** — Same as `callback`, but creates missing parent objects along the path.
252
+
253
+ ---
254
+
255
+ ## 🔌 Custom Backends
256
+
257
+ csonpath is designed to be backend-agnostic. It can work with any data structure that supports array, object, and scalar semantics.
258
+
259
+ 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.
260
+
261
+ For more details, see the existing backend implementations:
262
+ - `csonpath_json-c.h` — json-c backend
263
+ - `csonpath_python.c` — Python backend
264
+
265
+ ---
266
+
267
+ ## 🧪 Running Tests
268
+
269
+ ### C Tests
270
+ ```sh
271
+ make tests-c
272
+ ```
273
+
274
+ ### Python Tests
275
+ ```sh
276
+ make tests-py
277
+ ```
278
+
279
+ ### All Tests
280
+ ```sh
281
+ make tests
282
+ ```
283
+
284
+ ---
285
+
286
+ ## 📁 Directory Structure
287
+
288
+ | File / Directory | Description |
289
+ |---|---|
290
+ | `csonpath.h`, `csonpath_do.h` | Core implementation (header-only style) |
291
+ | `csonpath_json-c.h` | json-c backend |
292
+ | `csonpath_python.c` | Python C extension backend |
293
+ | `tests/` | C and Python test suites |
294
+ | `bench/` | Performance benchmarks |
295
+
296
+ ---
297
+
298
+ ## 🤝 Contributing
299
+
300
+ We welcome contributions!
301
+
302
+ Please read our [Contributing Guidelines](CONTRIBUTING.md) and [Code of Conduct](CODE_OF_CONDUCT.md) before submitting a pull request.
303
+
304
+ Feel free to open issues or pull requests!
305
+
306
+ ---
307
+
308
+ ## 📜 License
309
+
310
+ BSD 3-Clause. See [LICENSE](LICENSE).
@@ -0,0 +1,301 @@
1
+ # csonpath
2
+
3
+ > That's not my path, that's not your path, but **csonpath**.
4
+
5
+ [![Project Sandbox](https://docs.outscale.com/fr/userguide/_images/Project-Sandbox-yellow.svg)](https://docs.outscale.com/en/userguide/Open-Source-Projects.html)
6
+
7
+ **csonpath** is a partial [JSONPath](https://goessner.net/articles/JsonPath/) implementation in C, with Python bindings. It allows you to query, update, and remove data from JSON objects using path expressions.
8
+
9
+ 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 [json-c](https://github.com/json-c/json-c) and Python are provided.
10
+
11
+ ---
12
+
13
+ ## 🚀 Features
14
+
15
+ ### JSONPath Syntax
16
+
17
+ | Feature | Example | Description |
18
+ |---|---|---|
19
+ | Dot notation | `$.a.b` | Access nested object fields |
20
+ | Bracket notation | `$['a']['b']` | Alternative object/array access |
21
+ | Array index | `$.array[0]` | Access by zero-based index |
22
+ | Wildcard `[*]` | `$.array[*].field` | Iterate all array elements |
23
+ | Recursive descent `..` | `$..name` | Search recursively for a key |
24
+ | Union `,` (inside brackets) | `$['a','b']`, `$.array[0,1]`, `$.items[?n==1, ?n==2]` | Match all listed selectors at once |
25
+ | 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. |
26
+ | Filters | `$.items[?price > 10]` | Filter array elements |
27
+ | Regex filters | `$.items[?name =~ "foo"]` | POSIX regex-based filtering |
28
+ | Multiple filters (`&`) | `$.items[?a=1 & b=2]` | Combine conditions |
29
+ | Subpath expressions | `$.obj[$.key]` | Use JSON values as dynamic path keys |
30
+ | `@` current object | `$.items[?@.price > 10]` | Reference the current element in filters |
31
+
32
+ ### Operations
33
+ - **Find First** — retrieve the first match.
34
+ - **Find All** — retrieve all matches (returns an array).
35
+ - **Update or Create** — modify existing values or create missing ones.
36
+ - **Remove** — delete matching elements.
37
+ - **Callback** — execute a custom callback on each match.
38
+ - **Update or Create Callback** — traverse the path, creating missing intermediate objects and arrays, then invoke the callback on each leaf node.
39
+
40
+ ---
41
+
42
+ ## 🌐 Links
43
+
44
+ - Project website: https://github.com/outscale/csonpath
45
+ - Join our community on [Discord](https://discord.gg/HUVtY5gT6s)
46
+
47
+ ---
48
+
49
+ ## 📄 Table of Contents
50
+
51
+ - [Features](#-features)
52
+ - [Installation](#-installation)
53
+ - [Usage](#-usage)
54
+ - [C API Reference](#-c-api-reference)
55
+ - [Python API Reference](#-python-api-reference)
56
+ - [Custom Backends](#-custom-backends)
57
+ - [Running Tests](#-running-tests)
58
+ - [Directory Structure](#-directory-structure)
59
+ - [Contributing](#-contributing)
60
+ - [License](#-license)
61
+
62
+ ---
63
+
64
+ ## 📦 Installation
65
+
66
+ ### Prerequisites
67
+ - C compiler (`gcc` or `clang`)
68
+ - [json-c](https://github.com/json-c/json-c) library (for C usage)
69
+ - Python 3.x (for Python bindings)
70
+
71
+ ### C (json-c)
72
+ Just include the header in your project. There is no separate install step required:
73
+ ```c
74
+ #include "csonpath_json-c.h"
75
+ ```
76
+ Make sure to link against `json-c` when compiling:
77
+ ```sh
78
+ gcc myapp.c -o myapp $(pkg-config --cflags --libs json-c)
79
+ ```
80
+
81
+ ### Python
82
+ Install from PyPI:
83
+ ```sh
84
+ pip install csonpath
85
+ ```
86
+
87
+ To install from source (development):
88
+ ```sh
89
+ pip install .
90
+ # or
91
+ make pip-dev
92
+ ```
93
+
94
+ ---
95
+
96
+ ## 🛠️ Usage
97
+
98
+ ### C (json-c)
99
+
100
+ ```c
101
+ #include "csonpath_json-c.h"
102
+
103
+ static void my_cb(json_object *parent, struct csonpath_child_info *info,
104
+ json_object *current, void *ud)
105
+ {
106
+ json_object_set_string(current, "modified");
107
+ }
108
+
109
+ int main(void)
110
+ {
111
+ struct json_object *jobj = json_tokener_parse(json_str);
112
+ struct csonpath *p = csonpath_new("$.a");
113
+
114
+ /* Find First: return the first match, or NULL */
115
+ struct json_object *ret = csonpath_find_first(p, jobj);
116
+
117
+ /* Find All: return a NEW json_object array. Caller must free it. */
118
+ ret = csonpath_find_all(p, jobj);
119
+ json_object_put(ret);
120
+
121
+ /* Remove: delete matching keys (or set array slots to null). Returns count. */
122
+ int removed = csonpath_remove(p, jobj);
123
+
124
+ /* Update or Create: replace matches, or create the full path if missing. */
125
+ csonpath_update_or_create(p, jobj, json_object_new_string("new_value"));
126
+
127
+ /* Callback: call a user function for every match. */
128
+ csonpath_callback(p, jobj, my_cb, NULL);
129
+
130
+ /* Update or Create Callback: like callback, but creates missing parents first,
131
+ then invokes the callback on every leaf (existing or newly created). */
132
+ csonpath_update_or_create_callback(p, jobj, my_cb, NULL);
133
+
134
+ csonpath_destroy(p);
135
+ json_object_put(jobj);
136
+ return 0;
137
+ }
138
+ ```
139
+
140
+ ### Python
141
+
142
+ ```python
143
+ import csonpath
144
+
145
+ data = {"a": "value", "array": [1, 2, 3]}
146
+ p = csonpath.CsonPath("$.a")
147
+
148
+ # Find First / Find All
149
+ p.find_first(data) # -> "value"
150
+ p.find_all(data) # -> ["value"]
151
+
152
+ # Remove: returns number of removed items
153
+ p.set_path("$.array[*]")
154
+ p.remove(data)
155
+
156
+ # Update or Create: builds missing objects/arrays automatically
157
+ p.set_path("$.x.y.z")
158
+ p.update_or_create(data, [])
159
+ # data is now {"a": "value", "array": [1, 2, 3], "x": {"y": {"z": []}}}
160
+
161
+ # Callback
162
+ p.set_path("$.a")
163
+ p.callback(data, lambda parent, idx, cur, _: parent.__setitem__(idx, cur.upper()))
164
+
165
+ # Update or Create Callback: creates parents, then calls cb on each leaf
166
+ p.set_path("$[*].a")
167
+ p.update_or_create_callback(dst, my_sync_fn, userdata)
168
+ ```
169
+
170
+ ---
171
+
172
+ ## 📘 C API Reference
173
+
174
+ ```c
175
+ struct csonpath *csonpath_new(const char *path);
176
+ ```
177
+ Create and initialize a new csonpath object.
178
+
179
+ ```c
180
+ int csonpath_set_path(struct csonpath *p, const char *path);
181
+ ```
182
+ Change the path of an existing object.
183
+
184
+ ```c
185
+ int csonpath_compile(struct csonpath *p);
186
+ ```
187
+ Compile the path expression. This is optional—paths are compiled automatically on first use—but explicit compilation can help catch syntax errors earlier.
188
+
189
+ ```c
190
+ void csonpath_print_instruction(struct csonpath *p);
191
+ ```
192
+ Print the compiled bytecode instructions (useful for debugging).
193
+
194
+ ```c
195
+ struct json_object *csonpath_find_first(struct csonpath *p, struct json_object *json);
196
+ ```
197
+ Return the first matching value, or `NULL` if none is found.
198
+
199
+ ```c
200
+ struct json_object *csonpath_find_all(struct csonpath *p, struct json_object *json);
201
+ ```
202
+ Return a new `json_object` array containing all matches. **Must be freed with `json_object_put()`.**
203
+
204
+ ```c
205
+ int csonpath_remove(struct csonpath *p, struct json_object *json);
206
+ ```
207
+ Remove all matching elements. Returns the number of elements removed.
208
+
209
+ ```c
210
+ int csonpath_update_or_create(struct csonpath *p, struct json_object *json, struct json_object *new_val);
211
+ ```
212
+ Replace matching values with `new_val`, or create the path if it does not exist.
213
+
214
+ ```c
215
+ int csonpath_callback(struct csonpath *p, struct json_object *json,
216
+ json_c_callback callback, void *userdata);
217
+ ```
218
+ Invoke `callback` for every match.
219
+
220
+ ```c
221
+ int csonpath_update_or_create_callback(struct csonpath *p, struct json_object *json,
222
+ json_c_callback callback, void *userdata);
223
+ ```
224
+ Like `callback`, but traverses the path while updating/creating missing intermediate objects.
225
+
226
+ ```c
227
+ void csonpath_destroy(struct csonpath *p);
228
+ ```
229
+ Free the csonpath object.
230
+
231
+ ---
232
+
233
+ ## 📗 Python API Reference
234
+
235
+ - **`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 `$`.
236
+ - **`set_path(path)`** — Change the path expression.
237
+ - **`find_first(json)`** — Return the first match, or `None`.
238
+ - **`find_all(json)`** — Return a list of all matches, or `None` (or `[]` if configured).
239
+ - **`remove(json)`** — Remove all matches. Returns the number of removed items.
240
+ - **`update_or_create(json, value)`** — Replace matches with `value`, or create the path.
241
+ - **`callback(json, callback, callback_data=None)`** — Call `callback(parent, idx, current, callback_data)` for every match.
242
+ - **`update_or_create_callback(json, callback, callback_data=None)`** — Same as `callback`, but creates missing parent objects along the path.
243
+
244
+ ---
245
+
246
+ ## 🔌 Custom Backends
247
+
248
+ csonpath is designed to be backend-agnostic. It can work with any data structure that supports array, object, and scalar semantics.
249
+
250
+ 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.
251
+
252
+ For more details, see the existing backend implementations:
253
+ - `csonpath_json-c.h` — json-c backend
254
+ - `csonpath_python.c` — Python backend
255
+
256
+ ---
257
+
258
+ ## 🧪 Running Tests
259
+
260
+ ### C Tests
261
+ ```sh
262
+ make tests-c
263
+ ```
264
+
265
+ ### Python Tests
266
+ ```sh
267
+ make tests-py
268
+ ```
269
+
270
+ ### All Tests
271
+ ```sh
272
+ make tests
273
+ ```
274
+
275
+ ---
276
+
277
+ ## 📁 Directory Structure
278
+
279
+ | File / Directory | Description |
280
+ |---|---|
281
+ | `csonpath.h`, `csonpath_do.h` | Core implementation (header-only style) |
282
+ | `csonpath_json-c.h` | json-c backend |
283
+ | `csonpath_python.c` | Python C extension backend |
284
+ | `tests/` | C and Python test suites |
285
+ | `bench/` | Performance benchmarks |
286
+
287
+ ---
288
+
289
+ ## 🤝 Contributing
290
+
291
+ We welcome contributions!
292
+
293
+ Please read our [Contributing Guidelines](CONTRIBUTING.md) and [Code of Conduct](CODE_OF_CONDUCT.md) before submitting a pull request.
294
+
295
+ Feel free to open issues or pull requests!
296
+
297
+ ---
298
+
299
+ ## 📜 License
300
+
301
+ BSD 3-Clause. See [LICENSE](LICENSE).
@@ -21,12 +21,17 @@ for arg in $@; do
21
21
  CFLAGS="${CFLAGS} -DCSONPATH_PCRE2_POSIX $(pkg-config --cflags libpcre2-posix)"
22
22
  LDFLAGS="${LDFLAGS} $(pkg-config --libs libpcre2-posix)"
23
23
  fi
24
+ if [ "--asan" = $arg ]; then
25
+ CFLAGS="${CFLAGS} -fsanitize=address -fno-omit-frame-pointer"
26
+ LDFLAGS="${LDFLAGS} -fsanitize=address"
27
+ fi
24
28
  if [ "--help" = $arg ]; then
25
29
  echo "help"
26
30
  echo " --tiny-regex"
27
31
  echo " --pcre2-regex"
28
32
  echo " --pcre2-posix"
29
33
  echo " --no-regex"
34
+ echo " --asan"
30
35
  fi
31
36
  done
32
37