How to store a JsonArray in a Map? - java

I have used the following Code
JSONArray jsonArray = new JSONArray(incomingData);
Map<String,String> map=new HashMap<String,String>();
for(int i=0;i<jsonArray.length();i++){
JSONObject j = jsonArray.getJSONObject(i);
Iterator<?> it = j.keys();
while (it.hasNext()) {
String n = it.next().toString();
map.put(n, j.getString(n));
}
}
But when I execute this code it's throwing an Exception.
org.json.JSONException: JSONObject["low_stock_date"] not a string.
JsonArray Used
[
{
"item_id": "1",
"product_id": "1",
"stock_id": "1",
"qty": "99.0000",
"low_stock_date": null
},
{
"item_id": "2",
"product_id": "2",
"stock_id": "1",
"qty": "100.0000",
"low_stock_date": null
}
]
Please help me to over come this issue.

The value of low_stock_date is null. thats why it is throwing this error.
Just add one check for null before adding it into Map.
if(!j.isNull(n)){
map.put(n, j.getString(n));
}
else{
map.put(n, null);
}

for(int i=0;i<jsonArray.length();i++){
JSONObject j = jsonArray.getJSONObject(i);
Iterator<?> it = j.keys();
while (it.hasNext()) {
String n = it.next().toString();
map.put(n, j.getString(n));
}
}
You are doing the above, inside for loop & storing the key/value pair in Map.
As same keys are there in every JSONObject so only values in last jsonobject would be there in for Map keys.
As an alternate you can create an ArrayList for storing these Maps, which you can get while converting each JSONObject to Map
One more thing you can do here is using a parser like Jackson Parser
HashMap<String,String> result =
new ObjectMapper().readValue(JSON_SOURCE, HashMap.class);
(where JSON_SOURCE is a File, input stream, reader, or json content String).
That would be quick & better way of doing the what you want.

This is actually how the method is implemented:
/**
* Get the string associated with a key.
*
* #param key
* A key string.
* #return A string which is the value.
* #throws JSONException
* if there is no string value for the key.
*/
public String getString(String key) throws JSONException {
Object object = this.get(key);
if (object instanceof String) {
return (String) object;
}
throw new JSONException("JSONObject[" + quote(key) + "] not a string.");
}
You could check the type of the to-be-loaded object to see if it is null:
j.isNull(n);
and then act accordingly:
if (j.isNull(n)) {
// do something
} else {
// do something else
}

Related

How to parse a JSON string using ggson to get field values

I have a sample JSON as below. I need to get the individual fields like ASIdentifer and ExternalIdentifer. I have stored this JSON data in a string.
Using GoogleJson as the module(ggson)
JSON data:
{
"DeviceCommon": {
"ASIdentifier": "123",
"DatadeliveyMechanism": "notify",
"MobileOriginatorCallbackReference": {
"url": "http://application.example.com/inbound/notifications/modatanotification/"
},
"AccessiblityCallbackReference": {
"url": "http://application.example.com/inbound/notifications/accessibilitystatusnotification"
}
},
"DeviceList": [{
"ExternalIdentifer": "123456#mydomain.com",
"msisdn": "123456",
"senderName": "Device1",
"MobileOriginatorCallbackReference": {
"notifyURL": "http://application.example.com/inbound/notifications/modatanotification/"
},
"ConfigurationResultCallbackReference": {
"notifyURL": "http://application.example.com/inbound/notifications/configurationResult"
},
"ASreferenceID": "AS000001",
"NIDDduration": "1d"
}]
}
I created the POJO classes and parsed the data using below code
data = new Gson().fromJson(new FileReader("/home/raj/apache-tomcat-8.0.3/webapps/file.json"), Data.class);
System.out.println(data);
Output:
Data{
deviceCommon=DeviceCommon{
asIdentifier='123'
datadeliveyMechanism='notify'
mobileOriginatorCallbackReference=http://application.example.com/inbound/notifications/modatanotification/
accessiblityCallbackReference=http://application.example.com/inbound/notifications/accessibilitystatusnotification
}
deviceList=[DeviceListEntry{
externalIdentifer='123456#mydomain.com'
msisdn='123456'
senderName='Device1'
mobileOriginatorCallbackReference=http://application.example.com/inbound/notifications/modatanotification/
configurationResultCallbackReference=http://application.example.com/inbound/notifications/configurationResult
asReferenceID='AS000001'
nidDduration='1d'
}]
}
String jsonInString = gson.toJson(data);
System.out.println("String is"+ jsonInString);
Output:
String is{"DeviceCommon":{"ASIdentifier":"123","DatadeliveyMechanism":"notify","MobileOriginatorCallbackReference":{"url":"http://application.example.com/inbound/notifications/modatanotification/"},"AccessiblityCallbackReference":{"url":"http://application.example.com/inbound/notifications/accessibilitystatusnotification"}},"DeviceList":[{"ExternalIdentifer":"123456#mydomain.com","msisdn":"123456","senderName":"Device1","MobileOriginatorCallbackReference":{"notifyURL":"http://application.example.com/inbound/notifications/modatanotification/"},"ConfigurationResultCallbackReference":{"notifyURL":"http://application.example.com/inbound/notifications/configurationResult"},"ASreferenceID":"AS000001","NIDDduration":"1d"}]}
I need to parse this JSON string to get individual fields like ExternalIdentifier and ASIdentifier.
I tried something like this but it is not working.
JsonObject jobj = new Gson().fromJson(jsonInString, JsonObject.class);
String result = jobj.get("ASIdentifier").toString();
System.out.println("value is"+ result);
Note: ExternalIdentifier is within the array, so I need to loop through the array to find it.
Can you please tell me what I'm doing wrong?
Possible solution:
String result = jobj.get("DeviceCommon").getAsJsonObject().get("ASIdentifier").getAsString();
System.out.println("ASIdentifier: "+ result);
JsonArray jsonArray = jobj.get("DeviceList").getAsJsonArray();
for (JsonElement device : jsonArray ) {
result = device.getAsJsonObject().get("ExternalIdentifer").getAsString();
System.out.println("ExternalIdentifer: "+ result);
}
Output:
ASIdentifier: 123
ExternalIdentifer: 123456#mydomain.com
public static void printJson(JsonElement jsonElement,String key) {
// Check whether jsonElement is JsonObject or not
if (jsonElement.isJsonObject()) {
Set<Entry<String, JsonElement>> ens = ((JsonObject) jsonElement).entrySet();
if (ens != null) {
// Iterate JSON Elements with Key values
for (Entry<String, JsonElement> en : ens) {
// System.out.println("##key is"+en.getKey() + " : ");
printJson(en.getValue(), en.getKey());
// System.out.println(en.getValue().getAsString());
// System.out.println(jsonElement.getAsString());
}
}
}
// Check whether jsonElement is Primitive or not
else if (jsonElement.isJsonPrimitive()) {
// print value as String
System.out.println("###key is"+key);
System.out.println("### value is"+jsonElement.getAsString());
}
else if (jsonElement.isJsonArray()) {
JsonArray jarr = jsonElement.getAsJsonArray();
// Iterate JSON Array to JSON Elements
System.out.println("\n###Array size is"+ jarr.size());
for (JsonElement je : jarr) {
printJson(je,key);
}
}
}

GSON: Help me parse some unknown JSON data

Hi have JSON data that can come back with different data so I am writing deserialiser to parse it
So basically it come as this
{
"data": {
"errors": {
"user": [
"Invalid email, username or password. Please try again"
]
}
}
}
OR this
{
"errors": {
"promotion": [
"The code was invalid"
]
}
}
My goal is to extract the strings in the array, I do not know what the field will be called. All I do know is that it come as Data or errors or both then inside I have a unknown field name with an array of messages.
So I wrote a deserialiser
#Override
public APIErrorBody deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
String message = ""; /** The error message we are going to display */
/** Backend inconsistency: Unsuccessful response from backend can come with node data -> or errors -> we must check what is there */
JsonObject dataOrErrorsAsRoot = json.getAsJsonObject().get("data").getAsJsonObject();
if(dataOrErrorsAsRoot == null){ //If we do not have data node then lets check for errors
dataOrErrorsAsRoot = json.getAsJsonObject().get("errors").getAsJsonObject();
}
for (Map.Entry<String, JsonElement> entry : dataOrErrorsAsRoot.entrySet())
{
String key = entry.getKey();
JsonElement element = entry.getValue();
DebugUtils.Log(key + " " + element.toString());
if(element.isJsonArray()){
JsonArray jsonArray = element.getAsJsonArray();
for(int i = 0; i < jsonArray.size(); i ++){
message += jsonArray.get(i).getAsString() + "\n";
}
}
}
APIErrorBody apiErrorBody = new APIErrorBody();
apiErrorBody.setErrorMessageToDisplay(message);
DebugUtils.Log("the final message is " + apiErrorBody.getErrorMessageToDisplay());
return apiErrorBody;
}
The problem I have is here
for (Map.Entry<String, JsonElement> entry : dataOrErrorsAsRoot.entrySet())
{
String key = entry.getKey();
JsonElement element = entry.getValue();
DebugUtils.Log(key + " " + element.toString());
if(element.isJsonArray()){
JsonArray jsonArray = element.getAsJsonArray();
for(int i = 0; i < jsonArray.size(); i ++){
message += jsonArray.get(i).getAsString() + "\n";
}
}
}
So the problem is, its not a JSON array even though when I debug, it is. The value contained in the JsonElement 'element' is
{"user":["Invalid email, username or password. Please try again"]}
Which is an array
However, my debugger says this which confuses me a lot o_O
That element is a JSON object that contains a LinkedTreeMap
And that map has a value which a JSON array, how do I get it :)
Someone please help, I am dying
Well the debugger got it right, element.isJsonArray() is a hint that "user" is indeed JsonArray.
You simply got a LinkedTreeMap that is mapping between String keys to values. You give it a key and get a value. The value can be anything. In case of "user" key, the value is JsonArray.
You can use this code .lets say String responseStr=new JSONObject(jsondat);
JSONObject response = new JSONObject(responseStr);
if (response.has("data")) {
JSONObject data = response.getJSONObject("data");
JSONObject error = data.getJSONObject("errors");
JSONArray users = error.getJSONArray("user");
} else if (response.has("errors")) {
JSONObject error = response.getJSONObject("errors");
JSONArray promotion = error.getJSONArray("promotion");
}
I deleted my old answer, this is the new one.
Error on my part, see my backend can send data then errors or just data or just errors as the root nodes. I forgot to access data and then errors
This is the working code
#Override
public APIErrorBody deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
String message = ""; /** The error message we are going to display */
/** Backend inconsistency: Unsuccessful response from backend can come with node data -> errors or just errors or just data we must check what is there :S */
JsonObject dataOrErrorsAsRoot = json.getAsJsonObject();
if(dataOrErrorsAsRoot.has("data")){ //Check if the first element is data
dataOrErrorsAsRoot = json.getAsJsonObject().get("data").getAsJsonObject();
if(dataOrErrorsAsRoot.has("errors")){ //Check if an errors element exists in the data element
dataOrErrorsAsRoot = dataOrErrorsAsRoot.get("errors").getAsJsonObject();
}
}else{ //it must be errors only, but we should check in case a 503 happens and we try deserialise and empty body
if(dataOrErrorsAsRoot.has("errors")){
dataOrErrorsAsRoot = json.getAsJsonObject().get("errors").getAsJsonObject();
}
}
if(dataOrErrorsAsRoot != null){
for (Map.Entry<String, JsonElement> entry : dataOrErrorsAsRoot.entrySet()) //Lets loop through each field in the json object we got
{
JsonElement element = entry.getValue();
if(element.isJsonArray()){ //if its an array get each string message and combine it
JsonArray jsonArray = element.getAsJsonArray();
for(int i = 0; i < jsonArray.size(); i++){
message += jsonArray.get(i).getAsString() + "\n";
}
}
}
}
//Fail safe, if we got no message or no json just return some generic message
if(TextUtils.isEmpty(message)){
message = MyApplication.getInstance().getResources().getString(R.string.unknown_error);
}
APIErrorBody apiErrorBody = new APIErrorBody();
apiErrorBody.setErrorMessageToDisplay(message);
return apiErrorBody;
}

How to get dynamically changed Key's value in Json String using java

I'm trying to parse Json string using java, I have stuck up with some scenario.
See below is my JSON String:
"NetworkSettings": {
"Ports": {
"8080/tcp": [ // It will change dynamically like ("8125/udp" and "8080/udp" etc....)
{
"HostIp": "0.0.0.0",
"HostPort": "8080"
}
]
}
}
I try to parse the above json string by using the following code:
JsonObject NetworkSettings_obj=(JsonObject)obj.get("NetworkSettings");
if(NetworkSettings_obj.has("Ports"))
{
JsonObject ntw_Ports_obj=(JsonObject)NetworkSettings_obj.get("Ports");
if(ntw_Ports_obj.has("8080/tcp"))
{
JsonArray arr_ntwtcp=(JsonArray)ntw_Ports_obj.get("8080/tcp");
JsonObject ntwtcp_obj=arr_ntwtcp.get(0).getAsJsonObject();
if(ntwtcp_obj.has("HostIp"))
{
ntw_HostIp=ntwtcp_obj.get("HostIp").toString();
System.out.println("Network HostIp = "+ntw_HostIp);
}
if(ntwtcp_obj.has("HostPort"))
{
ntw_HostPort=ntwtcp_obj.get("HostPort").toString();
System.out.println("Network HostPort = "+ntw_HostPort);
}
}
else
{
ntw_HostIp="NA";
ntw_HostPort="NA";
}
}
else
{
ntw_HostIp="NA";
ntw_HostPort="NA";
}
In my code I have used this code
JsonArray arr_ntwtcp=(JsonArray)ntw_Ports_obj.get("8080/tcp");
to get the value of "8080/tcp"
How can I get the values of dynamically changing key like ("8125/udp","8134/udp", etc...)
Note: I'm using gson library for parsing
After modification
public static void main(String args[])
{
try
{
JsonParser parser = new JsonParser();
JsonObject obj=(JsonObject)parser.parse(new FileReader("sampleJson.txt"));
System.out.println("obj = "+obj);
JsonObject NetworkSettings_obj=(JsonObject)obj.get("NetworkSettings");
if(NetworkSettings_obj.has("Ports"))
{
JsonObject ntw_Ports_obj=(JsonObject)NetworkSettings_obj.get("Ports");
System.out.println("ntw_Ports_obj = "+ntw_Ports_obj);
Object keyObjects = new Gson().fromJson(ntw_Ports_obj, Object.class);
List keys = new ArrayList();
System.out.println(keyObjects instanceof Map); //**** here the statement prints false
if (keyObjects instanceof Map) // *** so controls doesn't enters into the if() condition block *** //
{
Map map = (Map) keyObjects;
System.out.println("Map = "+map);
keys.addAll(map.keySet());
String key = (String) keys.get(0);
JsonArray jArray = (JsonArray) ntw_Ports_obj.get(key);
System.out.println("Array List = "+jArray);
}
}
}
catch(Exception e)
{
}
}
You can do something like that (not tested but should be ok) :
if (ntw_Ports_obj.isJsonArray()) {
Iterator it = ntw_Ports_obj.getAsJsonArray().iterator();
while (it.hasNext()) {
JsonElement element = (JsonElement) it.next();
if(element.isJsonArray()){
JsonArray currentArray = element.getAsJsonArray();
// Do something with the new JsonArray...
}
}
}
So your problem is the key 8080/tcp is not fixed and it may change. when this situation you can try like this to get the value of the Dynamic key.
Set<Map.Entry<String, JsonElement>> entrySet = ntw_Ports_obj
.entrySet();
for (Map.Entry<String, JsonElement> entry : entrySet) {
String key = entry.getKey();
JsonArray jArray = (JsonArray) ntw_Ports_obj.get(key);
System.out.println(jArray);
}
Edit:
Object keyObjects = new Gson().fromJson(ntw_Ports_obj, Object.class);
List keys = new ArrayList();
/** for the given json there is a one json object within the 'Ports' so the 'keyObjects' will be the 'Map'**/
if (keyObjects instanceof Map) {
Map map = (Map) keyObjects;
keys.addAll(map.keySet());
/**
* keys is a List it may contain more than 1 value, but for the given
* json it will contain only one value
**/
String key = (String) keys.get(0);
JsonArray jArray = (JsonArray) ntw_Ports_obj.get(key);
System.out.println(jArray);
}

Convert JSON object with duplicate keys to JSON array

I have a JSON string that I get from a database which contains repeated keys. I want to remove the repeated keys by combining their values into an array.
For example
Input
{
"a":"b",
"c":"d",
"c":"e",
"f":"g"
}
Output
{
"a":"b",
"c":["d","e"],
"f":"g"
}
The actual data is a large file that may be nested. I will not know ahead of time what or how many pairs there are.
I need to use Java for this. org.json throws an exception because of the repeated keys, gson can parse the string but each repeated key overwrites the last one. I need to keep all the data.
If possible, I'd like to do this without editing any library code
As of today the org.json library version 20170516 provides accumulate() method that stores the duplicate key entries into JSONArray
JSONObject jsonObject = new JSONObject();
jsonObject.accumulate("a", "b");
jsonObject.accumulate("c", "d");
jsonObject.accumulate("c", "e");
jsonObject.accumulate("f", "g");
System.out.println(jsonObject);
Output:
{
"a":"b",
"c":["d","e"],
"f":"g"
}
I want to remove the repeated keys by combining their values into an array.
Think other than JSON parsing library. It's very simple Java Program using String.split() method that convert Json String into Map<String, List<String>> without using any library.
Sample code:
String jsonString = ...
// remove enclosing braces and double quotes
jsonString = jsonString.substring(2, jsonString.length() - 2);
Map<String, List<String>> map = new HashMap<String, List<String>>();
for (String values : jsonString.split("\",\"")) {
String[] keyValue = values.split("\":\"");
String key = keyValue[0];
String value = keyValue[1];
if (!map.containsKey(key)) {
map.put(key, new ArrayList<String>());
}
map.get(key).add(value);
}
output:
{
"f": ["g"],
"c": ["d","e"],
"a": ["b"]
}
In order to accomplish what you want, you need to create some sort of custom class since JSON cannot technically have 2 values at one key. Below is an example:
public class SomeClass {
Map<String, List<Object>> values = new HashMap<String, List<Object>>();
public void add(String key, Object o) {
List<Object> value = new ArrayList<Object>();
if (values.containsKey(key)) {
value = values.get(key);
}
value.add(o);
values.put(key, value);
}
public JSONObject toJson() throws JSONException {
JSONObject json = new JSONObject();
JSONArray tempArray = null;
for (Entry<String, List<Object>> en : values.entrySet()) {
tempArray = new JSONArray();
for (Object o : en.getValue()) {
tempArray.add(o);
}
json.put(en.getKey(), tempArray);
}
return json;
}
}
You can then retrieve the values from the database, call the .add(String key, Object o) function with the column name from the database, and the value (as the Object param). Then call .toJson() when you are finished.
Thanks to Mike Elofson and Braj for helping me in the right direction. I only wanted to have the keys with multiple values become arrays so I had to modify the code a bit. Eventually I want it to work for nested JSON as well, as it currently assumes it is flat. However, the following code works for what I need it for at the moment.
public static String repeatedKeysToArrays(String jsonIn) throws JSONException
{
//This assumes that the json is flat
String jsonString = jsonIn.substring(2, jsonIn.length() - 2);
JSONObject obj = new JSONObject();
for (String values : jsonString.split("\",\"")) {
String[] keyValue = values.split("\":\"");
String key = keyValue[0];
String value = "";
if (keyValue.length>1) value = keyValue[1];
if (!obj.has(key)) {
obj.put(key, value);
} else {
Object Oold = obj.get(key);
ArrayList<String> newlist = new ArrayList<String>();
//Try to cast as JSONArray. Otherwise, assume it is a String
if (Oold.getClass().equals(JSONArray.class)) {
JSONArray old = (JSONArray)Oold;
//Build replacement value
for (int i=0; i<old.length(); i++) {
newlist.add( old.getString(i) );
}
}
else if (Oold.getClass().equals(String.class)) newlist = new ArrayList<String>(Arrays.asList(new String[] {(String)Oold}));
newlist.add(value);
JSONArray newarr = new JSONArray( newlist );
obj.put(key,newarr);
}
}
return obj.toString();
}

Merge (Concat) Multiple JSONObjects in Java

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?
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.
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"}}
}
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.
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;
}
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}
}
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;
}
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;
}
}
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;
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
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);
}
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();
}
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.)
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;
}
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.
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;
}
Here is a version of https://stackoverflow.com/a/15070484/9897317 including JSONArrays :
/**
* 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 (value != null) {
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 if (value instanceof JSONArray) {
JSONArray arr = (JSONArray) value;
JSONArray newArr = new JSONArray();
for (int i = 0; i < arr.length(); i++) {
newArr.put(deepMerge(arr.getJSONObject(i), target.getJSONArray(key).getJSONObject(i)));
}
target.put(key, newArr);
} else {
target.put(key, value);
}
}
}
}
return target;
}

Categories