Jackson update part of object

5.2k Views Asked by At

This is what I am trying to do. lets say I have 2 json inputs.

First

{"testArr":[{"name":"name1"},{"name":"name2"}]}

Second

{"testArr":[{"name":"name1Changed"}]}

And I have classes

class test{
 public String name;
}

class runner{
  public ArrayList<test> testArr;
  public String firstJson;
  public String secondJson;

  public void runProj(){
    ObjectMapper mapper = new ObjectMapper();
    //This one is correct works just fine
    testArr = mapper.readValue(firstJson, ITBTeam.class);

    //Now I want to update just value of first element of array and 
    //tryed to use this function but this one will trim my array
     mapper.readerForUpdating(testArr).readValue(secondJson);
  }

}

So after this one I am getting

{"testArr":[{"name":"name1Changed"}]}

where I want to get

{"testArr":[{"name":"name1Changed"},{"name":"name2"}]}

Please let me know if I am missing something from Jackson or there are no such functionality and I would need to write my own.

Since there are no straight conversation to do what i wanted here is what i wrote for json nodes to do the same thing. Two functions to merger/overwrite nodes and merge/overwriteArray. This works by passing 2 json Nodes hope that will help if anyone are looking for similar things

 public static JsonNode mergeNode(JsonNode mainNode, JsonNode updateNode) {
    Iterator<String> fieldNames = updateNode.fieldNames();
    while (fieldNames.hasNext()) {

        String fieldName = fieldNames.next();
        JsonNode jsonNode = mainNode.get(fieldName);
        // if field exists and is an embedded object
        if (null != jsonNode && (jsonNode.isObject())) {
            mergeNode(jsonNode, updateNode.get(fieldName));
            // if field exists and is an embedded array
        } else if (null != jsonNode && (jsonNode.isArray())) {
            mergeArray(jsonNode, updateNode.get(fieldName));
        } else {
            // Overwrite field node               
            ((ObjectNode) mainNode).set(fieldName, updateNode.get(fieldName));
        }
    }
    return mainNode;
}

public static void mergeArray(JsonNode mainNode, JsonNode updateNode) {
    //Loops over array and adds items if they dont exist 
    //or updates existing value if it is not an object
    for (int i = 0; i < updateNode.size(); i++) {
        JsonNode jsonNode = mainNode.get(i);
        if (null != jsonNode && jsonNode.isObject()) {
            mergeNode(jsonNode, updateNode.get(i));
        } else if (null != jsonNode && (jsonNode.isArray())) {
            mergeArray(jsonNode, updateNode.get(i));
        } else if (null == jsonNode) {
            ((ArrayNode) mainNode).add(updateNode.get(i));
        } else {
            ((ArrayNode) mainNode).set(i, updateNode.get(i));
        }
    }       
}
2

There are 2 best solutions below

0
On

Your best bet is to read both document as JsonNodes, then traverse and update first document using logic you have in mind. Unfortunately there are many possible ways in which one might want to merge -- for example, in case of JSON arrays, should one or both be chosen; or should they be concatened; or should the entries be overridden by index (which is what you want) -- so Jackson does not have functionality for such processing.

0
On

Now you can make use of JSON-Patch https://github.com/fge/json-patch to merge the two JSON object/node

    // With Jackson
    final JsonMergePatch patch = mapper.readValue(in, JsonMergePatch.class);
    // With a JsonNode
    final JsonMergePatch patch = JsonMergePatch.fromJson(node);

    // orig is also a JsonNode
    final JsonNode patched = patch.apply(orig);