simple_jsonpath 0.2.2__cp314-cp314-win32.whl

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.
@@ -0,0 +1,7 @@
1
+
2
+ """A simple - yet quick - JSONPath implementation for querying JSON data."""
3
+
4
+ from .jsonpath import JsonPath, LocatedNode
5
+
6
+
7
+ __all__ = ["JsonPath", "LocatedNode"]
@@ -0,0 +1,16 @@
1
+ """A Python module for querying JSON data using JSONPath expressions."""
2
+
3
+
4
+ class SimpleJsonPath:
5
+ """A parser object that can be reused for multiple queries on the same JSON data."""
6
+ def __init__(self) -> None: ...
7
+ def set_data_from_json_str(self, input_data: str) -> None:
8
+ """Set the JSON data for the parser from a JSON string."""
9
+ ...
10
+ def find_from_set_data(self, path: str) -> str:
11
+ """Find the value(s) in the JSON data that match the given JSONPath expression, using a cache for parsed paths."""
12
+ ...
13
+
14
+ def find_location_from_set_data(self, path: str) -> str:
15
+ """Find the value(s) in the JSON data that match the given JSONPath expression, along with their locations, using a cache for parsed paths."""
16
+ ...
@@ -0,0 +1,144 @@
1
+ from ._simple_jsonpath import SimpleJsonPath as RustSimpleJsonPath
2
+ from typing import Any, Union
3
+ from dataclasses import dataclass
4
+ import builtins
5
+ import json
6
+
7
+ class PathComponentsIter:
8
+ def __init__(self, path: str, nodes: list[tuple[int, int]]):
9
+ self._current: int = 0
10
+ self._path: str = path
11
+ self._end: int = len(nodes)
12
+ self._items: list[tuple[int,int]] = nodes
13
+
14
+ def __next__(self) -> Union[str, int]:
15
+
16
+ if self._current >= self._end:
17
+ raise StopIteration
18
+ if self._current == 0:
19
+ self._current +=1
20
+ return "$"
21
+ else:
22
+ item = self._items[self._current]
23
+ if item[0] == 0:
24
+ self._current += 1
25
+ return item[1]
26
+ else:
27
+ self._current += 1
28
+ return self._path[item[0]:item[1]]
29
+
30
+ @dataclass(frozen=True)
31
+ class PathComponents:
32
+ _path: str
33
+ _items: list[tuple[int, int]]
34
+
35
+ def __len__(self) -> int:
36
+ return len(self._items)
37
+
38
+ def __iter__(self) -> PathComponentsIter:
39
+ return PathComponentsIter(self._path, self._items)
40
+
41
+ def __getitem__(self, index: int) -> Union[str, int]:
42
+ if index >= len(self._items):
43
+ raise IndexError("Index out of range")
44
+ elif index == 0:
45
+ return "$"
46
+ elif index < 0:
47
+ raise IndexError("Negative indexing is not supported")
48
+ else:
49
+ item = self._items[index]
50
+ if item[0] == 0:
51
+ return item[1]
52
+ else:
53
+ return self._path[item[0]:item[1]]
54
+ def __contains__(self, item: Union[int, str]) -> bool:
55
+ if isinstance(item, int):
56
+ for i in range(len(self._items)):
57
+ if i == 0:
58
+ continue
59
+ else:
60
+ if self._items[i][0] == 0 and self._items[i][1] == item:
61
+ return True
62
+ return False
63
+ else:
64
+ for i in range(len(self._items[1:])):
65
+ if i == 0:
66
+ if item == "$":
67
+ return True
68
+ else:
69
+ if self._path[self._items[i][0]:self._items[i][1]] == item:
70
+ return True
71
+ return False
72
+
73
+ class LocatedNode:
74
+ """A struct to hold the located nodes found from a located JSONPath query."""
75
+ def __init__(self, full_path: str, path_components: list[tuple[int, int]], node: Union[str,int,float,bool,None,dict[str, Any], list[Any]]) -> None:
76
+ self._full_path: str = full_path
77
+ self._path_components: PathComponents = PathComponents(full_path, path_components)
78
+ self._node: Union[str,int,float,bool,None,dict[str, Any], list[Any]] = node
79
+
80
+ @builtins.property
81
+ def path_components(self) -> PathComponents:
82
+ """An iterator that yields the path components of the last query result."""
83
+ return self._path_components
84
+ @builtins.property
85
+ def full_path(self) -> str:
86
+ """The full path of the last query result."""
87
+ return self._full_path
88
+ @builtins.property
89
+ def node(self) -> Union[str,int,float,bool,None,dict[str, Any], list[Any]]:
90
+ """The node value of the last query result."""
91
+ return self._node
92
+
93
+
94
+ class JsonPath:
95
+ """A simple JSONPath implementation for querying JSON data.
96
+
97
+ It uses a Rust backend for performance and supports caching of parsed
98
+ JSONPath expressions for repeated queries against the same JSON data.
99
+ """
100
+ def __init__(self) -> None:
101
+ self._parser = RustSimpleJsonPath()
102
+
103
+ def set_data(self, input_data: Union[dict[str, Any], list[Any]]) -> None:
104
+ """Set the JSON data for the parser from a Python dictionary or list.
105
+
106
+ Args:
107
+ input_data: The JSON data to set, as a Python dictionary or list.
108
+
109
+ Returns:
110
+ None
111
+
112
+ Raises:
113
+ ValueError: If the input data is not a valid JSON object or array.
114
+ """
115
+ self._parser.set_data_from_json_str(json.dumps(input_data))
116
+
117
+ def find(self, path: str) -> list[Any]:
118
+ """Find the value(s) in the JSON data that match the given JSONPath expression.
119
+
120
+ Args:
121
+ path: The JSONPath expression to evaluate.
122
+
123
+ Returns:
124
+ A list of values that match the JSONPath expression.
125
+
126
+ Raises:
127
+ ValueError: If the JSONPath expression is invalid.
128
+ """
129
+ return json.loads(self._parser.find_from_set_data(path))
130
+
131
+ def find_located(self, path: str) -> list[LocatedNode]:
132
+ """Find the value(s) in the JSON data that match the given JSONPath expression, along with their locations.
133
+
134
+ Args:
135
+ path: The JSONPath expression to evaluate.
136
+
137
+ Returns:
138
+ A list of LocatedNode objects that match the JSONPath expression.
139
+
140
+ Raises:
141
+ ValueError: If the JSONPath expression is invalid.
142
+ """
143
+ result = json.loads(self._parser.find_location_from_set_data(path))
144
+ return [LocatedNode(item['full_path'], item['path_components'], item['node']) for item in result]
File without changes
@@ -0,0 +1,173 @@
1
+ Metadata-Version: 2.4
2
+ Name: simple_jsonpath
3
+ Version: 0.2.2
4
+ Classifier: Intended Audience :: Developers
5
+ Classifier: Programming Language :: Rust
6
+ Classifier: Programming Language :: Python :: Implementation :: CPython
7
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Programming Language :: Python :: 3.14
14
+ License-File: LICENSE
15
+ Summary: A simple - yet quick - JSONPath implementation for querying JSON data.
16
+ Keywords: jsonpath
17
+ Author-email: Jeremy Spencer <seojumper@gmail.com>
18
+ License-Expression: MIT
19
+ Requires-Python: >=3.10, <3.15
20
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
21
+ Project-URL: Homepage, https://github.com/seojumper/simple_jsonpath
22
+
23
+ # simple_jsonpath
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ pip install simple_jsonpath
29
+ ```
30
+
31
+ ## About
32
+
33
+ This module is a JSONPath [RFC9535 - JSONPath: Query Expressions for JSON](https://datatracker.ietf.org/doc/html/rfc9535) utility library.
34
+
35
+ ## Use
36
+
37
+ This module exposes a single simple type - a ***JsonPath*** which has two methods after instantiation.
38
+
39
+ - ***set_data()***: sets the data that will be queried against. If multiple queries will be performed against a single piece of JSON data, this helps with the type conversion cost involved. This function can be called with providing new data whenever the inner data held is wished to be changed while retaining already complied paths (useful for querying multiple similarly structured documents).
40
+ - ***find()***: given a path that is wished to be found in the previously set data, this function will perform the query logic. Mulitple calls to **find()** will query against the previously 'set' data.
41
+ - ***find_located()***: given a path that is wished to be found in the previously set data, this function can return a list of ***LocatedNode*** objects. Each ***LocatedNode*** object will have attributes related to the path where the node was located as well as their corresponding data. This method is slower than ***find()***, so should ideally only be used when path information for the found nodes is needed.
42
+
43
+ ## Examples
44
+
45
+ ### 'Find' Example
46
+
47
+ ```python
48
+ from simple_jsonpath import JsonPath
49
+
50
+
51
+ json_data = {
52
+ "address": {
53
+ "prefix-list": [
54
+ {
55
+ "prefix": "2001:db8::1/64",
56
+ "eui-64": [
57
+ None
58
+ ]
59
+ }
60
+ ],
61
+ "link-local-address": [
62
+ {
63
+ "address": "fe80::1",
64
+ "link-local": [
65
+ None
66
+ ]
67
+ }
68
+ ]
69
+ }
70
+ }
71
+
72
+ # Instantiates the primary class
73
+ finder = JsonPath()
74
+
75
+ # Sets the data that is desired to be queried against
76
+ finder.set_data(json_data)
77
+
78
+ # A path is provided to query against the 'set' data. The path is internally parsed > used to qeury against the 'set' dataset.
79
+ # Notice that this implementaion allows for escaping of specials characters shorthand path syntax with single or double quotes
80
+ results = finder.find("$.address.'prefix-list'[*].prefix")
81
+
82
+ for data in results:
83
+ # Access the found node.
84
+ print(f"{data}")
85
+ # 2001:db8::1/64
86
+
87
+ ```
88
+
89
+ The inner implementation stores previously parsed 'paths'. This allows repeatedly used paths to bypass the parsing step invovled.
90
+
91
+ This is ideal for situations where multiple similar JSON documents will be searched in succession.
92
+
93
+ The same ***JsonPath*** object can then be reused with new data sets by calling ***set_data()*** on it again, and any previously parsed paths by the object will be retained.
94
+
95
+ Only when moving onto data of differing structure would it be potentially advisable to instantiate a new ***JsonPath*** object.
96
+
97
+ ### 'Find Located' Example
98
+
99
+ ```python
100
+ from simple_jsonpath import JsonPath, LocatedNode
101
+
102
+
103
+ json_data = {
104
+ "items": [
105
+ {
106
+ "address": {
107
+ "prefix-list": [
108
+ {
109
+ "prefix": "2001:db8::1/64",
110
+ "eui-64": [
111
+ None
112
+ ]
113
+ }
114
+ ],
115
+ "link-local-address": [
116
+ {
117
+ "address": "fe80::1",
118
+ "link-local": [
119
+ None
120
+ ]
121
+ }
122
+ ]
123
+ }
124
+ },
125
+ {
126
+ "address": {
127
+ "prefix-list": [
128
+ {
129
+ "prefix": "2001:db8::1/64",
130
+ "eui-64": [
131
+ None
132
+ ]
133
+ }
134
+ ],
135
+ "link-local-address": [
136
+ {
137
+ "address": "fe80::1",
138
+ "link-local": [
139
+ None
140
+ ]
141
+ }
142
+ ]
143
+ }
144
+ }
145
+ ]
146
+ }
147
+
148
+ # Instantiates the primary class
149
+ finder = JsonPath()
150
+
151
+ # Sets the data that is desired to be queried against
152
+ finder.set_data(json_data)
153
+
154
+ # Now we are interested in the path information where matches were found as well as the data
155
+ results: list[LocatedNode] = finder.find_located("$.items[*].address.'prefix-list'[*].prefix")
156
+
157
+ # Iterate through each found LocatedNode object
158
+ for data in results:
159
+
160
+ # Print the normalized full path where the node was found
161
+ print(f"{data.full_path}")
162
+ # $['items'][0]['address']['prefix-list'][0]['prefix']
163
+
164
+ # Iterate over the components of the found path
165
+ # Returned elements will either be a 'str' for keys or 'int' for index values
166
+ print(f"{', '.join([str(component) for component in data.path_components])}")
167
+ # $, items, 0, adddress, prefix-list, 0, prefix
168
+
169
+ # Access the found node.
170
+ print(f"{data.node}")
171
+ # 2001:db8::1/64
172
+ ```
173
+
@@ -0,0 +1,10 @@
1
+ simple_jsonpath/__init__.py,sha256=OofAgjl5Qzj6FaVVziW1JD8uhomAY6IhigeldJGbCrY,169
2
+ simple_jsonpath/_simple_jsonpath.cp314-win32.pyd,sha256=Ln0d1d56hm5OfQVTF3cJap2awgEc_l5My44pPB_JJmo,1455616
3
+ simple_jsonpath/_simple_jsonpath.pyi,sha256=xacGn97K908o7E1oYr4ZIdRgQuKr3W25rQCXPHB7R3M,791
4
+ simple_jsonpath/jsonpath.py,sha256=mfUInqb8qJ_wUbu95_MvM75zOlrRYnSOHjOOTRyBhnM,5294
5
+ simple_jsonpath/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ simple_jsonpath-0.2.2.dist-info/METADATA,sha256=jmMmt3Vr_rGG7V_utkoA39HtA36KoT2dwpRw-z9vVq8,6152
7
+ simple_jsonpath-0.2.2.dist-info/WHEEL,sha256=0RZTOoYFKCMMSuuhCfCow7TTDL6bY1Mq-hzasewFGUs,93
8
+ simple_jsonpath-0.2.2.dist-info/licenses/LICENSE,sha256=wFYBgkhw1xlkSCQiuXbjktNcYtLoxAOAnSVyKJRryOw,1081
9
+ simple_jsonpath-0.2.2.dist-info/sboms/simple_jsonpath.cyclonedx.json,sha256=UYAQhCBCpJo9CJO_xS3dJP-rSBIzN5qs65ULnoeD-3g,40961
10
+ simple_jsonpath-0.2.2.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: maturin (1.12.6)
3
+ Root-Is-Purelib: false
4
+ Tag: cp314-cp314-win32
@@ -0,0 +1,25 @@
1
+ Copyright (c) 2026 Jeremy Spencer
2
+
3
+ Permission is hereby granted, free of charge, to any
4
+ person obtaining a copy of this software and associated
5
+ documentation files (the "Software"), to deal in the
6
+ Software without restriction, including without
7
+ limitation the rights to use, copy, modify, merge,
8
+ publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software
10
+ is furnished to do so, subject to the following
11
+ conditions:
12
+
13
+ The above copyright notice and this permission notice
14
+ shall be included in all copies or substantial portions
15
+ of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
18
+ ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
19
+ TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
20
+ PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
21
+ SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
24
+ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25
+ DEALINGS IN THE SOFTWARE.