Merge (Concat) Multiple JSONObjects in Java

JavaJsonConcat

Java Problem Overview


I am consuming some JSON from two different sources, I end up with two JSONObjects and I'd like to combine them into one.

Data:

"Object1": {
    "Stringkey":"StringVal",
    "ArrayKey": [Data0, Data1]
}

"Object2": {
    "Stringkey":"StringVal",
    "Stringkey":"StringVal",
    "Stringkey":"StringVal",
}

Code, using http://json.org/java/ library:

// jso1 and jso2 are some JSONObjects already instantiated
JSONObject Obj1 = (JSONObject) jso.get("Object1");
JSONObject Obj2 = (JSONObject) jso.get("Object2");

So in this situation I'd like to combine Obj1 and Obj2, either to make a totally new JSONObject or concat one to the other. Any ideas besides pulling them all apart and individually adding in by puts?

Java Solutions


Solution 1 - Java

If you want a new object with two keys, Object1 and Object2, you can do:

JSONObject Obj1 = (JSONObject) jso1.get("Object1");
JSONObject Obj2 = (JSONObject) jso2.get("Object2");
JSONObject combined = new JSONObject();
combined.put("Object1", Obj1);
combined.put("Object2", Obj2);

If you want to merge them, so e.g. a top level object has 5 keys (Stringkey1, ArrayKey, StringKey2, StringKey3, StringKey4), I think you have to do that manually:

JSONObject merged = new JSONObject(Obj1, JSONObject.getNames(Obj1));
for(String key : JSONObject.getNames(Obj2))
{
  merged.put(key, Obj2.get(key));
}

This would be a lot easier if JSONObject implemented Map, and supported putAll.

Solution 2 - Java

In some cases you need a deep merge, i.e., merge the contents of fields with identical names (just like when copying folders in Windows). This function may be helpful:

/**
 * Merge "source" into "target". If fields have equal name, merge them recursively.
 * @return the merged object (target).
 */
public static JSONObject deepMerge(JSONObject source, JSONObject target) throws JSONException {
	for (String key: JSONObject.getNames(source)) {
			Object value = source.get(key);
			if (!target.has(key)) {
				// new value for "key":
				target.put(key, value);
			} else {
				// existing value for "key" - recursively deep merge:
				if (value instanceof JSONObject) {
					JSONObject valueJson = (JSONObject)value;
					deepMerge(valueJson, target.getJSONObject(key));
				} else {
					target.put(key, value);
				}
			}
	}
	return target;
}



/**
 * demo program
 */
public static void main(String[] args) throws JSONException {
	JSONObject a = new JSONObject("{offer: {issue1: value1}, accept: true}");
	JSONObject b = new JSONObject("{offer: {issue2: value2}, reject: false}");
	System.out.println(a+ " + " + b+" = "+JsonUtils.deepMerge(a,b));
	// prints:
	// {"accept":true,"offer":{"issue1":"value1"}} + {"reject":false,"offer":{"issue2":"value2"}} = {"reject":false,"accept":true,"offer":{"issue1":"value1","issue2":"value2"}}
}

Solution 3 - Java

You can create a new JSONObject like this:

JSONObject merged = new JSONObject();
JSONObject[] objs = new JSONObject[] { Obj1, Obj2 };
for (JSONObject obj : objs) {
    Iterator it = obj.keys();
    while (it.hasNext()) {
        String key = (String)it.next();
        merged.put(key, obj.get(key));
    }
}

With this code, if you have any repeated keys between Obj1 and Obj2 the value in Obj2 will remain. If you want the values in Obj1 to be kept you should invert the order of the array in line 2.

Solution 4 - Java

This wrapper method will help :

private static JSONObject merge(JSONObject... jsonObjects) throws JSONException {

    JSONObject jsonObject = new JSONObject();

    for(JSONObject temp : jsonObjects){
        Iterator<String> keys = temp.keys();
        while(keys.hasNext()){
            String key = keys.next();
            jsonObject.put(key, temp.get(key));
        }

    }
    return jsonObject;
}

Solution 5 - Java

Thanks to Erel. Here is a Gson version.

/**
 * Merge "source" into "target". If fields have equal name, merge them recursively.
 * Null values in source will remove the field from the target.
 * Override target values with source values
 * Keys not supplied in source will remain unchanged in target
 * 
 * @return the merged object (target).
 */
public static JsonObject deepMerge(JsonObject source, JsonObject target) throws Exception {
	
	for (Map.Entry<String,JsonElement> sourceEntry : source.entrySet()) {
		String key = sourceEntry.getKey();
		JsonElement value = sourceEntry.getValue();
	    if (!target.has(key)) {
	    	//target does not have the same key, so perhaps it should be added to target
	    	if (!value.isJsonNull()) //well, only add if the source value is not null
	    	target.add(key, value);
	    } else {
	    	if (!value.isJsonNull()) {
	    		if (value.isJsonObject()) {
	    			//source value is json object, start deep merge
	    			deepMerge(value.getAsJsonObject(), target.get(key).getAsJsonObject());
	    		} else {
	    			target.add(key,value);
	    		}
	    	} else {
	    		target.remove(key);
	    	}
	    }
	}
	return target;
}



/**
 * simple test
 */
public static void main(String[] args) throws Exception {
	JsonParser parser = new JsonParser();
	JsonObject a = null;
	JsonObject b = null;
	a = parser.parse("{offer: {issue1: null, issue2: null}, accept: true, reject: null}").getAsJsonObject();
	b = parser.parse("{offer: {issue2: value2}, reject: false}").getAsJsonObject();
    System.out.println(deepMerge(a,b));
    // prints:
	// {"offer":{},"accept":true}
	a = parser.parse("{offer: {issue1: value1}, accept: true, reject: null}").getAsJsonObject();
	b = parser.parse("{offer: {issue2: value2}, reject: false}").getAsJsonObject();
    System.out.println(deepMerge(a,b));
    // prints:
	// {"offer":{"issue2":"value2","issue1":"value1"},"accept":true}

}

Solution 6 - Java

A ready method to merge any number of JSONObjects:

/**
 * Merges given JSONObjects. Values for identical key names are merged 
 * if they are objects, otherwise replaced by the latest occurence.
 *
 * @param jsons JSONObjects to merge.
 *
 * @return Merged JSONObject.
 */
public static JSONObject merge(
  JSONObject[] jsons) {

  JSONObject merged = new JSONObject();
  Object parameter;

  for (JSONObject added : jsons) {

    for (String key : toStringArrayList(added.names())) {
      try {

        parameter = added.get(key);

        if (merged.has(key)) {
          // Duplicate key found:
          if (added.get(key) instanceof JSONObject) {
            // Object - allowed to merge:
            parameter =
              merge(
                new JSONObject[]{
                  (JSONObject) merged.get(key),
                  (JSONObject) added.get(key)});
          }
        }

        // Add or update value on duplicate key:
        merged.put(
          key,
          parameter);

      } catch (JSONException e) {
        e.printStackTrace();
      }
    }

  }

  return merged;
}

/**
 * Convert JSONArray to ArrayList<String>.
 *
 * @param jsonArray Source JSONArray.
 *
 * @return Target ArrayList<String>.
 */
public static ArrayList<String> toStringArrayList(JSONArray jsonArray) {

  ArrayList<String> stringArray = new ArrayList<String>();
  int arrayIndex;

  for (
    arrayIndex = 0;
    arrayIndex < jsonArray.length();
    arrayIndex++) {

    try {
      stringArray.add(
        jsonArray.getString(arrayIndex));
    } catch (JSONException e) {
      e.printStackTrace();
    }
  }

  return stringArray;
}

Solution 7 - Java

This is what I do

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

/**
 * This class has all static functions to merge 2 objects into one
 */
public class MergeHelper {
    private static ObjectMapper objectMapper = new ObjectMapper();

    /**
     * return a merge JsonNode, merge newJson into oldJson; override or insert
     * fields from newJson into oldJson
     * 
     * @param oldJson
     * @param newJson
     * @return
     */
    public static JsonNode mergeJsonObject(JsonNode oldJson, JsonNode newJson) {
        ObjectNode merged = objectMapper.createObjectNode();
        merged.setAll((ObjectNode) oldJson);
        merged.setAll((ObjectNode) newJson);
        return merged;
    }
}

Solution 8 - Java

For me that function worked:

private static JSONObject concatJSONS(JSONObject json, JSONObject obj) {
	JSONObject result = new JSONObject();
	
	for(Object key: json.keySet()) {
		System.out.println("adding " + key + " to result json");
		result.put(key, json.get(key));
	}
	
	for(Object key: obj.keySet()) {
		System.out.println("adding " + key + " to result json");
		result.put(key, obj.get(key));
	}
	
	return result;
}

(notice) - this implementation of concataion of json is for import org.json.simple.JSONObject;

Solution 9 - Java

In addition to @erel's answer, I had to make this edit (I'm using org.json.simple) to the outer else for dealing with JSONArray's:

            // existing value for "key" - recursively deep merge:
            if (value instanceof JSONObject) {
                JSONObject valueJson = (JSONObject)value;
                deepMerge(valueJson, (JSONObject) target.get(key));
            } 

            // insert each JSONArray's JSONObject in place
            if (value instanceof JSONArray) {
            	((JSONArray) value).forEach(
            		jsonobj ->
                	((JSONArray) target.get(key)).add(jsonobj));
            }
            else {
                target.put(key, value);
            }

Solution 10 - Java

I used string to concatenate new object to an existing object.


private static void concatJSON() throws IOException, InterruptedException {

	JSONParser parser = new JSONParser();
	Object obj = parser.parse(new FileReader(new File(Main.class.getResource("/file/user.json").toURI())));
	
	
	JSONObject jsonObj = (JSONObject) obj; //usernameJsonObj
	
	String [] values = {"0.9" , Date.from(Calendar.getInstance().toInstant()).toLocaleString()},
			innermost = {"Accomplished", "LatestDate"}, 
			inner = {"Lesson1", "Lesson2", "Lesson3", "Lesson4"};
	String in = "Jayvee Villa";

	JSONObject jo1 = new JSONObject();
	for (int i = 0; i < innermost.length; i++)
		jo1.put(innermost[i], values[i]);
	
	JSONObject jo2 = new JSONObject();
	for (int i = 0; i < inner.length; i++)
		jo2.put(inner[i], jo1);
	
	JSONObject jo3 = new JSONObject();
	jo3.put(in, jo2);

	String merger = jsonObj.toString().substring(0, jsonObj.toString().length()-1) + "," +jo3.toString().substring(1);
	
	System.out.println(merger);
	FileWriter pr = new FileWriter(file);
	pr.write(merger);
	pr.flush();
	pr.close();
}

Solution 11 - Java

Merging typed data structure trees is not trivial, you need to define the precedence, handle incompatible types, define how they will be casted and merged...

So in my opinion, you won't avoid >... pulling them all apart and individually adding in by puts`.

If your question is: Has someone done it for me yet? Then I think you can have a look at this YAML merging library/tool I revived. (YAML is a superset of JSON), and the principles are applicable to both.
(However, this particular code returns YAML objects, not JSON. Feel free to extend the project and send a PR.)

Solution 12 - Java

Today, I was also struggling to merge JSON objects and came with following solution (uses Gson library).

private JsonObject mergeJsons(List<JsonObject> jsonObjs) {
    JsonObject mergedJson = new JsonObject();
    jsonObjs.forEach((JsonObject jsonObj) -> {
        Set<Map.Entry<String, JsonElement>> entrySet = jsonObj.entrySet();
        entrySet.forEach((next) -> {
            mergedJson.add(next.getKey(), next.getValue());
        });
    });
    return mergedJson;
}

Solution 13 - Java

Somebody already mentioned above. I'll just post a short version.

To merge two JSONObject json1 & json2 You could simply deal it with String like this:

String merged = json1.toString().substring(0, json1.length() - 1) + "," +
json2.toString().substring(1);
JSONObject mergedJson = new JSONObject(merged);

Of course, do not forget deal with JSONException. :) Hope this could help you.

Solution 14 - Java

An improved version of merge on Gson's JsonObjects - can go any level of nested structure
 /**
 * Merge "source" into "target". 
 * 
 * <pre>
 *     An improved version of merge on Gson's JsonObjects - can go any level of nested structure:
 *             1. merge root & nested attributes.
 *             2. replace list of strings. For. eg.
 *              source -> "listOfStrings": ["A!"]
 *              dest -> "listOfStrings": ["A", "B"]
 *              merged -> "listOfStrings": ["A!", "B"]
 *             3. can merge nested objects inside list. For. eg.
 *              source -> "listOfObjects": [{"key2": "B"}]
 *              dest -> "listOfObjects": [{"key1": "A"}]
 *              merged -> "listOfObjects": [{"key1": "A"}, {"key2": "B"}]
 * </pre>
 * @return the merged object (target).
 */
public static JsonObject deepMerge(JsonObject source, JsonObject target) {
    for (String key: source.keySet()) {
        JsonElement srcValue = source.get(key);
        if (!target.has(key)) {
            target.add(key, srcValue);
        } else {
            if (srcValue instanceof JsonArray) {
                JsonArray srcArray = (JsonArray)srcValue;
                JsonArray destArray = target.getAsJsonArray(key);
                if (destArray == null || destArray.size() == 0) {
                    target.add(key, srcArray);
                    continue;
                } else {
                    IntStream.range(0, srcArray.size()).forEach(index -> {
                        JsonElement srcElem = srcArray.get(index);
                        JsonElement destElem = null;
                        if (index < destArray.size()) {
                            destElem = destArray.get(index);
                        }
                        if (srcElem instanceof JsonObject) {
                            if (destElem == null) {
                                destElem = new JsonObject();
                            }
                            deepMerge((JsonObject) srcElem, (JsonObject) destElem);
                        } else {
                            destArray.set(index, srcElem);
                        }
                    });
                }
            } else if (srcValue instanceof JsonObject) {
                JsonObject valueJson = (JsonObject)srcValue;
                deepMerge(valueJson, target.getAsJsonObject(key));
            } else {
                target.add(key, srcValue);
            }
        }
    }
    return target;
}

Solution 15 - Java

It's a while from the question but now JSONObject implements "toMap" method so you can try this way:

Map<String, Object> map = Obj1.toMap();      //making an HashMap from obj1
map.putAll(Obj2.toMap());                    //moving all the stuff from obj2 to map
JSONObject combined = new JSONObject( map ); //new json from map

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDanInDCView Question on Stackoverflow
Solution 1 - JavaMatthew FlaschenView Answer on Stackoverflow
Solution 2 - JavaErel Segal-HaleviView Answer on Stackoverflow
Solution 3 - JavaSanti P.View Answer on Stackoverflow
Solution 4 - JavaDave RanjanView Answer on Stackoverflow
Solution 5 - Javauser2651508View Answer on Stackoverflow
Solution 6 - JavaZonView Answer on Stackoverflow
Solution 7 - JavaAaron LamView Answer on Stackoverflow
Solution 8 - Javanoam aghaiView Answer on Stackoverflow
Solution 9 - JavajonayreyesView Answer on Stackoverflow
Solution 10 - JavaJayvee VillaView Answer on Stackoverflow
Solution 11 - JavaOndra ŽižkaView Answer on Stackoverflow
Solution 12 - JavaIsmetView Answer on Stackoverflow
Solution 13 - JavaKael luoView Answer on Stackoverflow
Solution 14 - JavaMaqbool AhmedView Answer on Stackoverflow
Solution 15 - Javalunix15View Answer on Stackoverflow