Interface FunctionExtension


public interface FunctionExtension

A read-only function extension (RFC 9535, Section 2.4). It runs with each JsonModel, also with a read-only model such as a model over a memory-mapped file.

A function extension adds a function to filter expressions. It has two parts:

  1. A FunctionSignature for the parser, which checks that each call is well-typed.
  2. This implementation for the evaluator, which applies the function.

Example

// first(NodesType): ValueType. The first node of a nodelist, or Nothing.
// A read-only extension: it runs with each JsonModel, also with a read-only model.
static final class First implements FunctionExtension {

    static final FunctionSignature SIGNATURE =
            new FunctionSignature("first", FunctionType.VALUE, List.of(FunctionType.NODES));

    @Override
    public FunctionSignature signature() {
        return SIGNATURE;
    }

    @Override
    public <N> Instance<N> apply(List<Instance<N>> arguments, JsonModel<N> model) {
        // The evaluator gives one instance for each parameter, of the declared type.
        List<Node<N>> nodes = ((Instance.NodesInstance<N>) arguments.getFirst()).nodes();
        // A ValueType result is an existing node, or Nothing.
        return new Instance.ValueInstance<>(nodes.isEmpty() ? Maybe.none() : Maybe.some(nodes.getFirst().value()));
    }
}

Give the signature to the parser and the implementation to the evaluator:

// The parser needs the signature, to check that queries are well-typed.
Result<JsonPathParser, FunctionRegistrationError> parser =
        JsonPathParser.withFunctions(List.of(First.SIGNATURE));
// The evaluator needs the implementation.
Result<JsonPathEvaluator, ExtensionError> evaluator =
        JsonPathEvaluator.withFunctions(List.of(new First()));

JsonPathQuery query = parser.orElse(JsonPathParser.standard())
        .parse("$[?first(@.*) > 4]")
        .orElse(null);
List<Node<JsonNode>> nodes = evaluator.orElse(JsonPathEvaluator.standard())
        .evaluate(query, root, JacksonJsonModel.INSTANCE)
        .orElse(List.of());
// nodes: the path $[1]

Arguments and results

A read-only extension cannot make new JSON values, because a read-only model cannot build nodes. So its signature has these limits:

Permitted
Parameters NodesType (Instance.NodesInstance) and LogicalType (Instance.LogicalInstance)
Result LogicalType, NodesType, or ValueType (Instance.ValueInstance)

A ValueType result must be a node of the query argument, for example a node of an argument nodelist, or Nothing. A NodesType result must have nodes of the query argument with their paths.

JsonPathEvaluator.withFunctions(List) rejects a read-only extension with a ValueType parameter: a ValueType argument can be a literal, which is not a node of the query argument. For such functions, use a BuildingFunctionExtension.

A result of the wrong type gives EvaluationError.ExtensionResultMismatch.

Rules

An implementation must have no side effects: the same arguments must always give the same result. It must not throw an exception. It reads node values only through the JsonModel that it receives.

  • Method Details

    • signature

      FunctionSignature signature()
      Returns the signature. The parser that makes the queries must know the same signature.
      Returns:
      the signature
    • apply

      <N> Instance<N> apply(List<Instance<N>> arguments, JsonModel<N> model)
      Applies the function.
      Type Parameters:
      N - the node type of the JSON model
      Parameters:
      arguments - one instance for each parameter, of the declared type of the parameter
      model - the model of the query argument, to read node values
      Returns:
      the result, an instance of the declared result type