Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Merge/Extend JSON Objects using Gson in Java

Oftentimes, I have a need to merge two JSON Objects (similar to the way jQuery's $.extend() works). However, the Gson library has no built in functionality and they have said they won't implement it.

Doing something like:

private void merge(JsonObject firstObj, JsonObject secondObj){
    for(String keyInSecondObj : secondObj.entrySet().keySet()) {
      if(!firstObj.has(keyInSecondObj )){
        firstObj.add(secondMap.get(keyInSecondObj));
    }
}

Is too simple because it doesn't handle recursively merging JsonObjects, doesn't handle conflicts when the key exists in both maps, and has no special handling for non-primitive values such as Arrays.

I failed to find any pre-built solutions to do this. I would prefer to use something that has been thoroughly tested instead of writing my own method, but it must be Gson (not Jackson or other).

Edit: I ended up writing my own implementation as have added as an answer to this question

This question is not a duplicate because it's not using Gson (or Java for that matter).

like image 983
bradvido Avatar asked Dec 04 '15 16:12

bradvido


People also ask

How do I merge two JSON objects in Java?

We can merge two JSON objects using the putAll() method (inherited from interface java. util.

How do I combine two Jsonarrays?

We can merge two JSON arrays using the addAll() method (inherited from interface java.

Does GSON offer parse () function?

The GSON JsonParser class can parse a JSON string or stream into a tree structure of Java objects. GSON also has two other parsers. The Gson JSON parser which can parse JSON into Java objects, and the JsonReader which can parse a JSON string or stream into tokens (a pull parser).

What is GSON toJson in Java?

Gson is the main actor class of Google Gson library. It provides functionalities to convert Java objects to matching JSON constructs and vice versa. Gson is first constructed using GsonBuilder and then toJson(Object) or fromJson(String, Class) methods are used to read/write JSON constructs.


2 Answers

Here's my first attempt at writing my own static merge method. Feel free to poke holes in it.

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import java.util.Map;

public class GsonTools {

    public static enum ConflictStrategy {

        THROW_EXCEPTION, PREFER_FIRST_OBJ, PREFER_SECOND_OBJ, PREFER_NON_NULL;
    }

    public static class JsonObjectExtensionConflictException extends Exception {

        public JsonObjectExtensionConflictException(String message) {
            super(message);
        }

    }

    public static void extendJsonObject(JsonObject destinationObject, ConflictStrategy conflictResolutionStrategy, JsonObject ... objs) 
            throws JsonObjectExtensionConflictException {
        for (JsonObject obj : objs) {
            extendJsonObject(destinationObject, obj, conflictResolutionStrategy);
        }
    }

    private static void extendJsonObject(JsonObject leftObj, JsonObject rightObj, ConflictStrategy conflictStrategy) 
            throws JsonObjectExtensionConflictException {
        for (Map.Entry<String, JsonElement> rightEntry : rightObj.entrySet()) {
            String rightKey = rightEntry.getKey();
            JsonElement rightVal = rightEntry.getValue();
            if (leftObj.has(rightKey)) {
                //conflict                
                JsonElement leftVal = leftObj.get(rightKey);
                if (leftVal.isJsonArray() && rightVal.isJsonArray()) {
                    JsonArray leftArr = leftVal.getAsJsonArray();
                    JsonArray rightArr = rightVal.getAsJsonArray();
                    //concat the arrays -- there cannot be a conflict in an array, it's just a collection of stuff
                    for (int i = 0; i < rightArr.size(); i++) {
                        leftArr.add(rightArr.get(i));
                    }
                } else if (leftVal.isJsonObject() && rightVal.isJsonObject()) {
                    //recursive merging
                    extendJsonObject(leftVal.getAsJsonObject(), rightVal.getAsJsonObject(), conflictStrategy);
                } else {//not both arrays or objects, normal merge with conflict resolution
                    handleMergeConflict(rightKey, leftObj, leftVal, rightVal, conflictStrategy);
                }
            } else {//no conflict, add to the object
                leftObj.add(rightKey, rightVal);
            }
        }
    }

    private static void handleMergeConflict(String key, JsonObject leftObj, JsonElement leftVal, JsonElement rightVal, ConflictStrategy conflictStrategy) 
            throws JsonObjectExtensionConflictException {
        {
            switch (conflictStrategy) {
                case PREFER_FIRST_OBJ:
                    break;//do nothing, the right val gets thrown out
                case PREFER_SECOND_OBJ:
                    leftObj.add(key, rightVal);//right side auto-wins, replace left val with its val
                    break;
                case PREFER_NON_NULL:
                    //check if right side is not null, and left side is null, in which case we use the right val
                    if (leftVal.isJsonNull() && !rightVal.isJsonNull()) {
                        leftObj.add(key, rightVal);
                    }//else do nothing since either the left value is non-null or the right value is null
                    break;
                case THROW_EXCEPTION:
                    throw new JsonObjectExtensionConflictException("Key " + key + " exists in both objects and the conflict resolution strategy is " + conflictStrategy);
                default:
                    throw new UnsupportedOperationException("The conflict strategy " + conflictStrategy + " is unknown and cannot be processed");
            }
        }
    }
}
like image 160
bradvido Avatar answered Oct 15 '22 08:10

bradvido


You can use

  Map firstObject = new GSON().fromJson(json1, HashMap.class);
  Map secondObject = new GSON().fromJson(json2, HashMap.class);

// merge Map firstObject and secondObject as you want, see this post

  String resultJson = new GSON().toJson(resultMap); 
like image 20
Slava Vedenin Avatar answered Oct 15 '22 09:10

Slava Vedenin