Class JacksonJsonModel

java.lang.Object
ca.marcusdunn.jsonlens.jackson.JacksonJsonModel
All Implemented Interfaces:
JsonEditor<tools.jackson.databind.JsonNode>, JsonFactory<tools.jackson.databind.JsonNode>, JsonModel<tools.jackson.databind.JsonNode>

public final class JacksonJsonModel extends Object implements JsonModel<tools.jackson.databind.JsonNode>, JsonFactory<tools.jackson.databind.JsonNode>, JsonEditor<tools.jackson.databind.JsonNode>

A JsonModel for Jackson 3 JsonNode values.

The model reads the nodes of the tree directly. It does not copy them, so each result Normalized Path leads to a node of your own tree. The model has no state: use INSTANCE.

// 1. Read the JSON value with your own JSON library. Keep numbers exact.
JsonMapper mapper = JsonMapper.builder()
        .enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
        .build();
JsonNode root = mapper.readTree(BOOKSTORE);

// 2. Parse the query. A query that is not valid gives an error value.
Result<JsonPathQuery, ParseError> parsed =
        JsonPathParser.standard().parse("$.store.book[?@.price < 10].title");

// 3. Apply the query to the value through a JsonModel.
switch (parsed) {
    case Result.Ok(JsonPathQuery query) -> {
        Result<List<Node<JsonNode>>, EvaluationError> result =
                JsonPathEvaluator.standard().evaluate(query, root, JacksonJsonModel.INSTANCE);
        switch (result) {
            case Result.Ok(List<Node<JsonNode>> nodes) -> {
                for (Node<JsonNode> node : nodes) {
                    lines.add(node.path() + " = " + node.value());
                }
            }
            case Result.Err(EvaluationError error) -> lines.add("Overflow: " + error.message());
        }
    }
    case Result.Err(ParseError error) -> lines.add("Bad query: " + error.message());
}
// lines:
//   $['store']['book'][0]['title'] = "Sayings of the Century"
//   $['store']['book'][2]['title'] = "Moby Dick"

Exact numbers

By default, Jackson reads a JSON number with a fraction or an exponent as a double. Such a number is not exact, and a number outside the range of double becomes infinite. To keep all numbers exact, enable DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS on the mapper:

JsonMapper mapper = JsonMapper.builder()
        .enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
        .build();

Changes

The model also implements JsonEditor: it changes ObjectNode and ArrayNode values in place.

Member order

Jackson keeps the members of an object in document order, so the evaluator gives object members in document order.

Nodes that are not JSON

A Jackson tree can hold values that JSON cannot represent. The model classifies them as follows:

  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static final JacksonJsonModel
    The model.
  • Method Summary

    Modifier and Type
    Method
    Description
    tools.jackson.databind.JsonNode
    array(List<tools.jackson.databind.JsonNode> elements)
    Builds an array.
    int
    arrayLength(tools.jackson.databind.JsonNode array)
    Returns the number of elements in an array.
    tools.jackson.databind.JsonNode
    bool(boolean value)
    Builds true or false.
    Maybe<tools.jackson.databind.JsonNode>
    element(tools.jackson.databind.JsonNode array, int index)
    Returns the element at a zero-based index.
    boolean
    hasDuplicate(tools.jackson.databind.JsonNode object, JsonString name)
    An ObjectNode is a map, so it cannot hold duplicate names.
    void
    insertElement(tools.jackson.databind.JsonNode array, int index, tools.jackson.databind.JsonNode value)
    Inserts an element.
    kind(tools.jackson.databind.JsonNode node)
    Returns the kind of a node.
    Maybe<tools.jackson.databind.JsonNode>
    member(tools.jackson.databind.JsonNode object, JsonString name)
    Returns the value of the member with a given name.
    int
    memberCount(tools.jackson.databind.JsonNode object)
    Returns the number of members in an object.
    MemberCursor<tools.jackson.databind.JsonNode>
    memberCursor(tools.jackson.databind.JsonNode object)
    Returns a cursor over the members of an object.
    tools.jackson.databind.JsonNode
    Builds null.
    Maybe<tools.jackson.databind.JsonNode>
    Builds a number.
    numberValue(tools.jackson.databind.JsonNode number)
    Returns the value of a number.
    tools.jackson.databind.JsonNode
    object(List<Property<tools.jackson.databind.JsonNode>> members)
    Builds an object.
    Maybe<tools.jackson.databind.JsonNode>
    putMember(tools.jackson.databind.JsonNode object, JsonString name, tools.jackson.databind.JsonNode value)
    Sets the value of a member.
    tools.jackson.databind.JsonNode
    removeElement(tools.jackson.databind.JsonNode array, int index)
    Removes an element.
    Maybe<tools.jackson.databind.JsonNode>
    removeMember(tools.jackson.databind.JsonNode object, JsonString name)
    Removes a member.
    tools.jackson.databind.JsonNode
    setElement(tools.jackson.databind.JsonNode array, int index, tools.jackson.databind.JsonNode value)
    Replaces an element.
    tools.jackson.databind.JsonNode
    Builds a string.
    stringValue(tools.jackson.databind.JsonNode string)
    Returns the value of a string.

    Methods inherited from class Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

    Methods inherited from interface JsonFactory

    copyOf

    Methods inherited from interface JsonModel

    compareNumbers, equal, members
  • Field Details

    • INSTANCE

      public static final JacksonJsonModel INSTANCE
      The model. It has no state.
  • Method Details

    • kind

      public JsonKind kind(tools.jackson.databind.JsonNode node)
      Description copied from interface: JsonModel
      Returns the kind of a node.
      Specified by:
      kind in interface JsonModel<tools.jackson.databind.JsonNode>
      Parameters:
      node - a node
      Returns:
      the kind of the node
    • arrayLength

      public int arrayLength(tools.jackson.databind.JsonNode array)
      Description copied from interface: JsonModel
      Returns the number of elements in an array.
      Specified by:
      arrayLength in interface JsonModel<tools.jackson.databind.JsonNode>
      Parameters:
      array - a node of kind JsonKind.ARRAY
      Returns:
      the number of elements, zero or more
    • element

      public Maybe<tools.jackson.databind.JsonNode> element(tools.jackson.databind.JsonNode array, int index)
      Description copied from interface: JsonModel
      Returns the element at a zero-based index.
      Specified by:
      element in interface JsonModel<tools.jackson.databind.JsonNode>
      Parameters:
      array - a node of kind JsonKind.ARRAY
      index - a zero-based index. It can be outside the array.
      Returns:
      the element, or Maybe.None if the index is outside the array
    • memberCount

      public int memberCount(tools.jackson.databind.JsonNode object)
      Description copied from interface: JsonModel

      Returns the number of members in an object.

      The default method counts the members of the cursor. A model overrides it if it knows the number without a walk. The result must be the number of members of JsonModel.memberCursor(Object).

      Specified by:
      memberCount in interface JsonModel<tools.jackson.databind.JsonNode>
      Parameters:
      object - a node of kind JsonKind.OBJECT
      Returns:
      the number of members, zero or more
    • member

      public Maybe<tools.jackson.databind.JsonNode> member(tools.jackson.databind.JsonNode object, JsonString name)
      Description copied from interface: JsonModel

      Returns the value of the member with a given name.

      Names are equal only if they have the same Unicode scalar values. The model must not apply Unicode normalization or case folding (RFC 9535, Section 2.3.1.2).

      The evaluator gives a name from the query, or a name from the cursor of this model or another model.

      The default method walks the cursor, and gives the value of the first member with the name. A model overrides it to find the member faster, for example in a hash map. The result must be equal to the result of the default method.

      Specified by:
      member in interface JsonModel<tools.jackson.databind.JsonNode>
      Parameters:
      object - a node of kind JsonKind.OBJECT
      name - a member name
      Returns:
      the member value, or Maybe.None if the object has no member with the name
    • hasDuplicate

      public boolean hasDuplicate(tools.jackson.databind.JsonNode object, JsonString name)
      An ObjectNode is a map, so it cannot hold duplicate names.
      Specified by:
      hasDuplicate in interface JsonModel<tools.jackson.databind.JsonNode>
      Parameters:
      object - a node of kind JsonKind.OBJECT
      name - a member name
      Returns:
      true if the object has two or more members with the name
    • memberCursor

      public MemberCursor<tools.jackson.databind.JsonNode> memberCursor(tools.jackson.databind.JsonNode object)
      Description copied from interface: JsonModel

      Returns a cursor over the members of an object.

      The cursor is the one way to read the members: the default methods for objects use it. The order of the cursor is the order in which the evaluator selects the children of the object. RFC 9535 does not specify this order. The order must be the same each time the method is called for the same object. The cursor gives each member, also a member with the name of an earlier member.

      The names can read the representation directly, as JsonModel.stringValue(Object) does. The evaluator keeps a name in the Normalized Path of each result node, so a name must stay valid while the values of the model are valid.

      Specified by:
      memberCursor in interface JsonModel<tools.jackson.databind.JsonNode>
      Parameters:
      object - a node of kind JsonKind.OBJECT
      Returns:
      a new cursor before the first member
    • stringValue

      public JsonString stringValue(tools.jackson.databind.JsonNode string)
      Description copied from interface: JsonModel

      Returns the value of a string.

      The result can read the representation directly. For a String, use JsonString.of(String).

      Specified by:
      stringValue in interface JsonModel<tools.jackson.databind.JsonNode>
      Parameters:
      string - a node of kind JsonKind.STRING
      Returns:
      the value of the string
    • numberValue

      public JsonNumber numberValue(tools.jackson.databind.JsonNode number)
      Description copied from interface: JsonModel

      Returns the value of a number.

      The model chooses the representation. For the common types, use the factories of JsonNumber, for example JsonNumber.of(long).

      Specified by:
      numberValue in interface JsonModel<tools.jackson.databind.JsonNode>
      Parameters:
      number - a node of kind JsonKind.NUMBER
      Returns:
      the value of the number
    • string

      public tools.jackson.databind.JsonNode string(JsonString value)
      Description copied from interface: JsonFactory
      Builds a string.
      Specified by:
      string in interface JsonFactory<tools.jackson.databind.JsonNode>
      Parameters:
      value - the value
      Returns:
      a node of kind JsonKind.STRING
    • number

      public Maybe<tools.jackson.databind.JsonNode> number(JsonNumber value)
      Description copied from interface: JsonFactory

      Builds a number.

      A representation can have a limit. For example, a BigDecimal cannot hold a number with an exponent outside the range of int. Then the result is Maybe.None: the factory never builds a number with a different value.

      Specified by:
      number in interface JsonFactory<tools.jackson.databind.JsonNode>
      Parameters:
      value - the value
      Returns:
      a node of kind JsonKind.NUMBER with the exact value, or Maybe.None if the representation cannot hold the value
    • bool

      public tools.jackson.databind.JsonNode bool(boolean value)
      Description copied from interface: JsonFactory
      Builds true or false.
      Specified by:
      bool in interface JsonFactory<tools.jackson.databind.JsonNode>
      Parameters:
      value - the value
      Returns:
      a node of kind JsonKind.TRUE or JsonKind.FALSE
    • nullValue

      public tools.jackson.databind.JsonNode nullValue()
      Description copied from interface: JsonFactory
      Builds null.
      Specified by:
      nullValue in interface JsonFactory<tools.jackson.databind.JsonNode>
      Returns:
      a node of kind JsonKind.NULL
    • array

      public tools.jackson.databind.JsonNode array(List<tools.jackson.databind.JsonNode> elements)
      Description copied from interface: JsonFactory
      Builds an array.
      Specified by:
      array in interface JsonFactory<tools.jackson.databind.JsonNode>
      Parameters:
      elements - the elements, in order
      Returns:
      a node of kind JsonKind.ARRAY
    • object

      public tools.jackson.databind.JsonNode object(List<Property<tools.jackson.databind.JsonNode>> members)
      Description copied from interface: JsonFactory
      Builds an object.
      Specified by:
      object in interface JsonFactory<tools.jackson.databind.JsonNode>
      Parameters:
      members - the members, in order. The names are unique.
      Returns:
      a node of kind JsonKind.OBJECT
    • putMember

      public Maybe<tools.jackson.databind.JsonNode> putMember(tools.jackson.databind.JsonNode object, JsonString name, tools.jackson.databind.JsonNode value)
      Description copied from interface: JsonEditor
      Sets the value of a member. If the object has a member with the name, the method replaces its value. Otherwise, it adds a member.
      Specified by:
      putMember in interface JsonEditor<tools.jackson.databind.JsonNode>
      Parameters:
      object - a node of kind JsonKind.OBJECT
      name - the member name
      value - the new value
      Returns:
      the previous value, or Maybe.None if the object had no member with the name
    • removeMember

      public Maybe<tools.jackson.databind.JsonNode> removeMember(tools.jackson.databind.JsonNode object, JsonString name)
      Description copied from interface: JsonEditor
      Removes a member.
      Specified by:
      removeMember in interface JsonEditor<tools.jackson.databind.JsonNode>
      Parameters:
      object - a node of kind JsonKind.OBJECT
      name - the member name
      Returns:
      the removed value, or Maybe.None if the object had no member with the name
    • insertElement

      public void insertElement(tools.jackson.databind.JsonNode array, int index, tools.jackson.databind.JsonNode value)
      Description copied from interface: JsonEditor
      Inserts an element. The elements at and after the index move one position to the right.
      Specified by:
      insertElement in interface JsonEditor<tools.jackson.databind.JsonNode>
      Parameters:
      array - a node of kind JsonKind.ARRAY
      index - the index of the new element: from 0 to the length of the array. The length appends the element.
      value - the new element
    • setElement

      public tools.jackson.databind.JsonNode setElement(tools.jackson.databind.JsonNode array, int index, tools.jackson.databind.JsonNode value)
      Description copied from interface: JsonEditor
      Replaces an element.
      Specified by:
      setElement in interface JsonEditor<tools.jackson.databind.JsonNode>
      Parameters:
      array - a node of kind JsonKind.ARRAY
      index - the index of the element: from 0 to the length of the array minus 1
      value - the new element
      Returns:
      the previous element
    • removeElement

      public tools.jackson.databind.JsonNode removeElement(tools.jackson.databind.JsonNode array, int index)
      Description copied from interface: JsonEditor
      Removes an element. The elements after the index move one position to the left.
      Specified by:
      removeElement in interface JsonEditor<tools.jackson.databind.JsonNode>
      Parameters:
      array - a node of kind JsonKind.ARRAY
      index - the index of the element: from 0 to the length of the array minus 1
      Returns:
      the removed element