Skip to content

Commit

Permalink
feat: JSONPointer.parent
Browse files Browse the repository at this point in the history
  • Loading branch information
jg-rp committed Jul 15, 2023
1 parent b7e4159 commit 763b6ff
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 11 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@

**Fixes**

- Fixed a bug with the parsing of JSON Pointers. When given an arbitrary string without slashes, `JSONPointer` would resolve to the document root. The empty string is the only valid pointer that should resolve to the document root. See [#27](https://github.com/jg-rp/python-jsonpath/issues/27).
- Fixed a bug with the parsing of JSON Pointers. When given an arbitrary string without slashes, `JSONPointer` would resolve to the document root. The empty string is the only valid pointer that should resolve to the document root. We now raise a `JSONPointerError` in such cases. See [#27](https://github.com/jg-rp/python-jsonpath/issues/27).

**Features**

- Added a command line interface, exposing JSONPath, JSON Pointer and JSON Patch features.
- Added `JSONPointer.parent()`, a method that returns a the parent of the pointer, as a new `JSONPointer`.

## Version 0.8.1

Expand Down
39 changes: 29 additions & 10 deletions jsonpath/pointer.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def __init__(
unicode_escape=unicode_escape,
uri_decode=uri_decode,
)
self._s = pointer
self._s = self._encode(self.parts)

def __str__(self) -> str:
return self._s
Expand Down Expand Up @@ -215,7 +215,7 @@ def resolve_parent(
JSONPointerTypeError: When attempting to resolve a non-index string
path part against a sequence.
"""
if not len(self.parts):
if not self.parts:
return (None, self.resolve(data))

if isinstance(data, str):
Expand All @@ -232,20 +232,26 @@ def resolve_parent(
except (JSONPointerIndexError, JSONPointerKeyError):
return (parent, UNDEFINED)

@staticmethod
def _encode(parts: Iterable[Union[int, str]]) -> str:
if parts:
return "/" + "/".join(
str(p).replace("~", "~0").replace("/", "~1") for p in parts
)
return ""

@classmethod
def from_match(
cls,
match: JSONPathMatch,
) -> JSONPointer:
"""Return a JSON Pointer for the path from a JSONPathMatch instance."""
# A rfc6901 string representation of match.parts.
if not match.parts:
if match.parts:
pointer = cls._encode(match.parts)
else:
# This should not happen, unless the JSONPathMatch has been tampered with.
pointer = ""
else:
pointer = "/" + "/".join(
str(p).replace("~", "~0").replace("/", "~1") for p in match.parts
)

return cls(
pointer,
Expand Down Expand Up @@ -285,9 +291,7 @@ def from_parts(
__parts = tuple(_parts)

if __parts:
pointer = "/" + "/".join(
p.replace("~", "~0").replace("/", "~1") for p in __parts
)
pointer = cls._encode(__parts)
else:
pointer = ""

Expand All @@ -308,6 +312,21 @@ def is_relative_to(self, other: JSONPointer) -> bool:
def __eq__(self, other: object) -> bool:
return isinstance(other, JSONPointer) and self.parts == other.parts

def parent(self) -> JSONPointer:
"""Return this pointer's parent, as a new `JSONPointer`.
If this pointer points to the document root, self is returned.
"""
if not self.parts:
return self
parent_parts = self.parts[:-1]
return JSONPointer(
self._encode(parent_parts),
parts=parent_parts,
unicode_escape=False,
uri_decode=False,
)


def resolve(
pointer: Union[str, Iterable[Union[str, int]]],
Expand Down
23 changes: 23 additions & 0 deletions tests/test_json_pointer.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,3 +219,26 @@ def test_pointer_with_leading_whitespace() -> None:
data = {"some": {"thing": [1, 2, 3]}}
pointer = JSONPointer(" /some/thing/0")
assert pointer.resolve(data) == 1
assert str(pointer) == "/some/thing/0"


def test_pointer_parent() -> None:
data = {"some": {"thing": [1, 2, 3]}}
pointer = JSONPointer("/some/thing/0")
assert pointer.resolve(data) == 1

parent = pointer.parent()
assert str(parent) == "/some/thing"
assert parent.resolve(data) == [1, 2, 3]

parent = parent.parent()
assert str(parent) == "/some"
assert parent.resolve(data) == {"thing": [1, 2, 3]}

parent = parent.parent()
assert str(parent) == ""
assert parent.resolve(data) == {"some": {"thing": [1, 2, 3]}}

parent = parent.parent()
assert str(parent) == ""
assert parent.resolve(data) == {"some": {"thing": [1, 2, 3]}}

0 comments on commit 763b6ff

Please sign in to comment.