简体   繁体   中英

How to create a JSON merge patch document for two objects with Jackson

We have an application with a significant investment in Jackson for JSON processing. Specifically there are many annotated model objects used in REST APIs. Now we need to call a REST API where PATCH wants a merge patch JSON document. Given the original object and the changed object, this is trivial to create with JSON-P, but I have not found a way to do it with Jackson.

This code works and does the job:

    public <T> String createMergePatch(T source, T target) throws IOException {
        var sourceBytes = objectMapper.writeValueAsBytes(source);
        var targetBytes = objectMapper.writeValueAsBytes(target);

        // Changing framework - not at all ideal
        var sourceJson = Json.createReader(new ByteArrayInputStream(sourceBytes)).readValue();
        var targetJson = Json.createReader(new ByteArrayInputStream(targetBytes)).readValue();
        var mergePatch = Json.createMergeDiff(sourceJson, targetJson);
        return mergePatch.toJsonValue().toString();
    }

The Jackson annotations are used when the source and target objects are converted into JSON. Then, we are switching frameworks to get something that createMergeDiff can handle. It is inefficient and we need to include dependencies we don't want, but the output is correct.

Finally, my question is if there is a better way to do this with Jackson, without having to pull in another JSON framework and without having to round-trip between JSON and objects?

Here is an example what I think you asked for.

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

import java.io.IOException;

public class Main {
public static void main(String[] args) throws IOException {
    
    String json1 = "{\"name\":\"John\",\"age\":30,\"city\":\"New York\"}";
    String json2 = "{\"name\":\"John\",\"age\":30,\"city\":\"New York\",\"country\":\"USA\"}";
    String result = createMergePatchWith(json1, json2);
    System.out.println(result);

}

public static String createMergePatchWith(String json1, String json2) throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    JsonNode jsonNode1 = mapper.readTree(json1);
    JsonNode jsonNode2 = mapper.readTree(json2);
    JsonNode mergePatch = doMergeWithJackson(jsonNode1, jsonNode2);
    return mapper.writeValueAsString(mergePatch);
}

private static JsonNode doMergeWithJackson(JsonNode jsonNode1, JsonNode jsonNode2) {
    if (jsonNode1.isObject() && jsonNode2.isObject()) {
        ObjectNode objectNode = (ObjectNode) jsonNode1;
        jsonNode2.fields().forEachRemaining(entry -> {
            JsonNode value = entry.getValue();
            if (value.isObject()) {
                objectNode.set(entry.getKey(), doMergeWithJackson(objectNode.get(entry.getKey()), value));
            } else {
                objectNode.set(entry.getKey(), value);
            }
        });
        return objectNode;
    }
    return jsonNode2;
}

}

Output of this: {"name":"John","age":30,"city":"New York","country":"USA"}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM