Class JsonPointer

java.lang.Object
ca.marcusdunn.jsonlens.pointer.JsonPointer

public final class JsonPointer extends Object

A JSON Pointer (RFC 6901): a sequence of reference tokens that identifies a value in a JSON document.

A pointer has two text forms. The JSON string form (Section 5) is / followed by each token, with ~ written as ~0 and / written as ~1. The URI fragment form (Section 6) is # followed by the UTF-8 bytes of the string form, with percent-encoding.

JsonNode document = JsonMapper.builder().build().readTree("""
        {"foo": ["bar", "baz"], "a/b": 1}""");

// Parse a pointer. A '/' in a name is written as "~1".
JsonPointer pointer = JsonPointer.parse("/a~1b").orElse(JsonPointer.root());

// Resolve it with the model of your JSON library. The result is your own node.
switch (pointer.resolve(document, JacksonJsonModel.INSTANCE)) {
    case Result.Ok<JsonNode, PointerError>(JsonNode value) -> lines.add("value: " + value);
    case Result.Err<JsonNode, PointerError>(PointerError error) -> lines.add(error.message());
}
// value: 1

// An error is a value, not an exception.
Result<JsonNode, PointerError> missing =
        JsonPointer.parse("/foo/2").orElse(JsonPointer.root()).resolve(document, JacksonJsonModel.INSTANCE);
// missing: Err[error=IndexOutOfRange[parent=/foo, token=2]]

// The URI fragment form has percent-encoding.
Maybe<String> fragment = JsonPointer.of(List.of("c%d")).toFragment(); // Some[value=#/c%25d]

resolve(Object, JsonModel) reads the caller's nodes through a JsonModel, so it works with any JSON library and makes no copy of a value. An error is a PointerError value, not an exception.

Two pointers are equal if they have the same tokens.

  • Method Details

    • root

      public static JsonPointer root()
      Returns the empty pointer, which references the whole document.
      Returns:
      the pointer ""
    • of

      public static JsonPointer of(List<String> tokens)
      Returns the pointer with the given reference tokens. A token can be any string.
      Parameters:
      tokens - the tokens, without escapes. No token is null.
      Returns:
      the pointer
    • parse

      public static Result<JsonPointer, PointerError> parse(String text)
      Parses the JSON string form of a pointer (RFC 6901, Sections 3 and 5).
      Parameters:
      text - the pointer, for example /a~1b/0. JSON escapes such as \" must already be decoded.
      Returns:
      the pointer, or a PointerError.MissingSlash or PointerError.InvalidEscape
    • parse

      public static Result<JsonPointer, PointerError> parse(JsonString text)
      Parses the JSON string form of a pointer from a string of a model, for example the path of a JSON Patch operation. It reads the string once and makes no other copy of it.
      Parameters:
      text - the pointer
      Returns:
      the pointer, or a PointerError.MissingSlash or PointerError.InvalidEscape
    • parseFragment

      public static Result<JsonPointer, PointerError> parseFragment(String fragment)
      Parses the URI fragment form of a pointer (RFC 6901, Section 6), for example #/c%25d.
      Parameters:
      fragment - the fragment identifier, with its #
      Returns:
      the pointer, or a PointerError
    • tokens

      public List<String> tokens()
      Returns the reference tokens, without escapes.
      Returns:
      the tokens, in order. The list cannot be changed.
    • isRoot

      public boolean isRoot()
      Tells if this is the empty pointer, which references the whole document.
      Returns:
      true if the pointer has no tokens
    • parent

      public Maybe<JsonPointer> parent()
      Returns the pointer to the parent: all tokens except the last.
      Returns:
      the parent, or Maybe.None for the empty pointer
    • lastToken

      public Maybe<String> lastToken()
      Returns the last reference token.
      Returns:
      the last token, or Maybe.None for the empty pointer
    • append

      public JsonPointer append(String token)
      Returns this pointer with one more token.
      Parameters:
      token - the token, without escapes
      Returns:
      the longer pointer
    • isProperPrefixOf

      public boolean isProperPrefixOf(JsonPointer other)
      Tells if this pointer is a proper prefix of another pointer: the other pointer references a value inside the value of this pointer. For example, /a is a proper prefix of /a/b, but not of /a or /ab.
      Parameters:
      other - another pointer
      Returns:
      true if this pointer is a proper prefix of the other pointer
    • isArrayIndex

      public static boolean isArrayIndex(String token)
      Tells if a token has the syntax of an array index (RFC 6901, Section 4): 0, or ASCII digits without a leading zero. The index can be larger than any array.
      Parameters:
      token - a reference token
      Returns:
      true if the token is an array index
    • arrayIndex

      public static Maybe<Integer> arrayIndex(String token)
      Reads an array index token (RFC 6901, Section 4): 0, or digits without a leading zero.
      Parameters:
      token - a reference token
      Returns:
      the index, or Maybe.None if the token is not an array index (see isArrayIndex(String)), or if the index is larger than the largest int. No array has an element at such an index.
    • resolve

      public <N> Result<N, PointerError> resolve(N root, JsonModel<N> model)

      Resolves this pointer against a document (RFC 6901, Section 4).

      Each token selects a member of an object, by equal Unicode code points without normalization, or an element of an array, by its index. The method reads the caller's nodes directly and makes no copy.

      Type Parameters:
      N - the node type of the model
      Parameters:
      root - the root value of the document
      model - the model of the document
      Returns:
      the referenced value, or the PointerError of the first token that does not resolve
    • resolvePath

      public <N> Result<List<N>, PointerError> resolvePath(N root, JsonModel<N> model)

      Resolves this pointer, and returns each value along the path: the root, the value of the first token, and so on to the referenced value.

      A caller that builds a changed copy of a document needs the containers on the path. The method reads the caller's nodes directly and makes no copy of a value.

      Type Parameters:
      N - the node type of the model
      Parameters:
      root - the root value of the document
      model - the model of the document
      Returns:
      the values, one more than the tokens, or the PointerError of the first token that does not resolve
    • toFragment

      public Maybe<String> toFragment()

      Returns the URI fragment form (RFC 6901, Section 6): # and the UTF-8 bytes of the string form, with percent-encoding for each byte that a fragment does not permit.

      UTF-8 encodes only Unicode scalar values. A token can also hold an unpaired surrogate, for example from the JSON escape ?. Such a pointer has no fragment form.

      Returns:
      the fragment identifier, for example #/c%25d, or Maybe.None if a token has an unpaired surrogate
    • toString

      public String toString()
      Returns the JSON string form (RFC 6901, Section 5): / before each token, with ~ as ~0 and / as ~1.
      Overrides:
      toString in class Object
      Returns:
      the pointer, for example /a~1b/0
    • equals

      public boolean equals(@Nullable Object other)
      Overrides:
      equals in class Object
    • hashCode

      public int hashCode()
      Overrides:
      hashCode in class Object