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).
We can merge two JSON objects using the putAll() method (inherited from interface java. util.
We can merge two JSON arrays using the addAll() method (inherited from interface java.
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).
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.
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");
}
}
}
}
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With