Package ca.marcusdunn.jsonlens.patch


@NullMarked package ca.marcusdunn.jsonlens.patch

RFC 6902 JSON Patch: JsonPatch, its operations (Operation), and its errors (PatchError).

// Keep numbers exact, and reject duplicate names, so that a patch with two "op" members fails.
JsonMapper mapper = JsonMapper.builder()
        .enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
        .enable(StreamReadFeature.STRICT_DUPLICATE_DETECTION)
        .build();
JsonNode document = mapper.readTree("""
        {"baz": "qux", "foo": "bar"}""");
JsonNode patchDocument = mapper.readTree("""
        [{"op": "replace", "path": "/baz", "value": "boo"},
         {"op": "add", "path": "/hello", "value": ["world"]},
         {"op": "remove", "path": "/foo"}]""");

// Read the patch with any model, and apply it in place with an editable model.
Result<JsonNode, PatchError> result = JsonPatch.parse(patchDocument, JacksonJsonModel.INSTANCE)
        .flatMap(patch -> patch.apply(document, JacksonJsonModel.INSTANCE));
switch (result) {
    case Result.Ok<JsonNode, PatchError>(JsonNode root) -> lines.add("patched: " + root);
    case Result.Err<JsonNode, PatchError>(PatchError error) -> lines.add(error.message());
}
// patched: {"baz":"boo","hello":["world"]}

// A patch is atomic: after an error, the document has its original value.
JsonNode failing = mapper.readTree("""
        [{"op": "replace", "path": "/baz", "value": 42},
         {"op": "test", "path": "/baz", "value": "C"}]""");
Result<JsonNode, PatchError> failed = JsonPatch.parse(failing, JacksonJsonModel.INSTANCE)
        .flatMap(patch -> patch.apply(document, JacksonJsonModel.INSTANCE));
// failed: Err[error=TestFailed[operation=1]], document: {"baz":"boo","hello":["world"]}
  • Class
    Description
    A JSON Patch (RFC 6902): a sequence of operations that changes a JSON document.
    An operation of a JSON Patch (RFC 6902, Section 4).
    add (Section 4.1): adds a member, inserts an array element, or replaces the whole document.
    copy (Section 4.5): adds a copy of the value at from at the target location.
    move (Section 4.4): removes the value at from and adds it at the target location.
    remove (Section 4.2): removes the value at the target location.
    replace (Section 4.3): replaces the value at the target location.
    test (Section 4.6): tests that the value at the target location is equal to a value.
    An error of a JSON Patch: a patch document that violates RFC 6902, or an operation that is not successful (RFC 6902, Section 5).
    An operation has a member more than once.
    The from location of a move or a copy does not exist (Sections 4.4 and 4.5).
    The path or from member of an operation is not a valid JSON Pointer.
    An operation has no member that it needs: op and path (Section 4), value for add, replace, and test, or from for move and copy.
    The from location of a move is a proper prefix of its path (Section 4.4).
    The patch document is not an array (RFC 6902, Section 3).
    An operation is not an object (RFC 6902, Section 3).
    The op, path, or from member of an operation is not a string.
    The target location does not exist.
    A remove has the whole document as its target.
    The value at the target location of a test is not equal to its value (Section 4.6).
    The op of an operation is not one of the six operations (Section 4).
    The value of an operation cannot be added to the target document: the target model cannot hold one of its numbers, for example 1e99999999999 in a Jackson tree, or an object of the value has duplicate member names, which have no defined value (RFC 8259, Section 4).