Replacing a value in a javax.json.JsonObject ist not possible directly because javax.json.JsonObject implements an immutable map. In order to do that you have to create a new JsonObject and copy the values from the original one into the new one taking care of replacing the value you want to replace.
I found examples of how to do that with "simple" JsonObject, where there are no nested JsonObjects. What I'm looking for is a general replace implementation where I pass a JsonObject, the attribute name and the new value. This method should "traverse" the JsonObject and replace the attribute (wherever in the object hierarchy it is) and leave the others attributes unchanged.
For ex. this is my original JsonObject
{
"Attr1":number1,
"Attr2":number2,
"Attr3":number3,
"Attr4":[
"string1"
],
"Attr5":[
{
"Attr6":[
{
"Attr7":"string2",
"Attr8":"string3",
"$Attr9":number4
},
{
"Attr7":"string4",
"Attr8":"string5",
"Attr9":number5
}
],
"Attr10":number6,
"Attr14":{
"Attr10":"string6",
"Attr11":"string7",
"Attr12":"string8"
},
"Attr13":[
"string9",
"string10"
],
"Attr14":"string11"
}
]
}
and I want to replace the Attr6 with just an array of strings instead of an array of JsonObjects:
"Attr6":["newString1","newString2"],
The corresponding call could be something like replaceValue(JsonObject jObj, String attrName, JsonValue newValue)) where 'jObj' is the entire Json, 'attrName' is 'Attr6' and 'newValue' is a JsonArray containing the two strings.
Can someone point me to an example where such a feature is implemented or help me with it?
I tried by myself with this, but it doesn't really work because the builder is re-created on every recursive iteration (or just more probably because it is all wrong... :) )
public static JsonObject replaceValue( final JsonObject jsonObject, final String jsonKey, final JsonValue jsonValue )
{
JsonObjectBuilder builder = Json.createObjectBuilder();
if(jsonObject == null)
{
return builder.build();
}
Iterator<Entry<String, JsonValue>> it = jsonObject.entrySet().iterator();
while (it.hasNext())
{
#SuppressWarnings( "rawtypes" )
JsonObject.Entry mapEntry = it.next();
if (mapEntry.getKey() == jsonKey)
{
builder.add(jsonKey, jsonValue);
}
else if (ValueType.STRING.equals(((JsonValue) mapEntry.getValue()).getValueType()) || ValueType.NUMBER.equals(((JsonValue) mapEntry.getValue()).getValueType()) || ValueType.TRUE.equals(((JsonValue) mapEntry.getValue()).getValueType()) ||
ValueType.FALSE.equals(((JsonValue) mapEntry.getValue()).getValueType()) || (JsonValue) mapEntry.getValue() == null || "schemas".equalsIgnoreCase((String) mapEntry.getKey()))
{
builder.add(mapEntry.getKey().toString(), (JsonValue) mapEntry.getValue());
}
else if (ValueType.OBJECT.equals(((JsonValue) mapEntry.getValue()).getValueType()))
{
JsonObject modifiedJsonobject = (JsonObject) mapEntry.getValue();
if (modifiedJsonobject != null)
{
replaceValue(modifiedJsonobject, jsonKey, jsonValue);
}
}
else if (ValueType.ARRAY.equals(((JsonValue) mapEntry.getValue()).getValueType()))
{
for (int i = 0; i < ((JsonValue) mapEntry.getValue()).asJsonArray().size(); i++)
{
replaceValue((JsonObject) ((JsonValue) mapEntry.getValue()).asJsonArray().get(i), jsonKey, jsonValue);
}
}
}
return builder.build();
}
This is an alternative approach to solving the problem, one which uses the streaming parser provide by javax.json.stream.JsonParser. This generates a stream of tokens from the JSON source, with javax.json.stream.JsonParser.Event values which describe the type of token (e.g. START_OBJECT, KEY_NAME, and so on).
Most importantly for us, there are skipObject() and skipArray() methods on the parser, which allow us to cut out the unwanted section of our source JSON.
The overall approach is to build a new version of the JSON, token-by-token, as a string, substituting the replacement section when we reach the relevant location (or multiple locations) in the JSON.
Finally, we convert the new string back to an object, so we can pretty-print it.
There is no recursion used in this approach.
import java.io.IOException;
import javax.json.Json;
import javax.json.stream.JsonParser;
import javax.json.stream.JsonParser.Event;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import javax.json.JsonObject;
import javax.json.JsonWriterFactory;
import javax.json.stream.JsonGenerator;
public class StreamDemo {
public static void doStream() throws IOException {
JsonParser jsonParser = Json.createParser(new StringReader(JSONSTRING));
StringBuilder sb = new StringBuilder();
Event previous = null;
String targetKeyName = "Attr6";
String replacement = "[\"newString1\",\"newString2\"]";
// This event reflects the end of the "replacement" string - namely "]".
// We need this because this event may be different from the replaced event.
Event replacementPreviousEvent = Event.END_ARRAY;
// Used when we find the target key for replacement:
boolean doReplacement = false;
while (jsonParser.hasNext()) {
Event event = jsonParser.next();
if (doReplacement) {
// Skip over the structure we want to replace:
if (event.equals(Event.START_OBJECT)) {
jsonParser.skipObject();
} else if (event.equals(Event.START_ARRAY)) {
jsonParser.skipArray();
}
// Write the replacement fragment here:
sb.append(replacement);
// Move to the next event in the stream:
event = jsonParser.next();
previous = replacementPreviousEvent;
doReplacement = false;
}
if (Event.KEY_NAME.equals(event)
&& jsonParser.getString().equals(targetKeyName)) {
doReplacement = true;
}
switch (event) {
case START_OBJECT:
if (Event.END_OBJECT.equals(previous)) {
sb.append(",");
}
sb.append("{");
break;
case END_OBJECT:
sb.append("}");
break;
case START_ARRAY:
sb.append("[");
break;
case END_ARRAY:
sb.append("]");
break;
case KEY_NAME:
sb = previousWasAValue(previous, sb);
sb = previousWasAnEnd(previous, sb);
sb.append("\"").append(jsonParser.getString()).append("\":");
break;
case VALUE_STRING:
sb = previousWasAValue(previous, sb);
sb.append("\"").append(jsonParser.getString()).append("\"");
break;
case VALUE_NUMBER:
sb = previousWasAValue(previous, sb);
if (jsonParser.isIntegralNumber()) {
sb.append(jsonParser.getLong());
} else {
sb.append(jsonParser.getBigDecimal().toPlainString());
}
break;
case VALUE_TRUE:
sb = previousWasAValue(previous, sb);
sb.append("true");
break;
case VALUE_FALSE:
sb = previousWasAValue(previous, sb);
sb.append("false");
break;
case VALUE_NULL:
sb = previousWasAValue(previous, sb);
sb.append("null");
break;
default:
break;
}
previous = event;
}
// At the end, pretty-print the new JSON:
JsonObject modifiedObject = Json.createReader(new StringReader(sb.toString())).readObject();
Map<String, Boolean> config = new HashMap<>();
config.put(JsonGenerator.PRETTY_PRINTING, true);
String jsonString;
JsonWriterFactory writerFactory = Json.createWriterFactory(config);
try ( Writer writer = new StringWriter()) {
writerFactory.createWriter(writer).write(modifiedObject);
jsonString = writer.toString();
}
System.out.println(jsonString);
}
private static StringBuilder previousWasAValue(Event previous, StringBuilder sb) {
// The current value follows another value - so a separating comma is needed:
if (Event.VALUE_STRING.equals(previous)
|| Event.VALUE_NUMBER.equals(previous)
|| Event.VALUE_TRUE.equals(previous)
|| Event.VALUE_FALSE.equals(previous)
|| Event.VALUE_NULL.equals(previous)) {
sb.append(",");
}
return sb;
}
private static StringBuilder previousWasAnEnd(Event previous, StringBuilder sb) {
// The current key follows the end of an object or an array, so a
// separating comma is needed:
if (Event.END_OBJECT.equals(previous)
|| Event.END_ARRAY.equals(previous)) {
sb.append(",");
}
return sb;
}
private static final String JSONSTRING
= """
{
"Attr0": null,
"Attr1": true,
"Attr2": false,
"Attr3": 3,
"Attr4": [
"string1"
],
"Attr5": [{
"Attr6": [{
"Attr7": "string2",
"Attr8": "string3",
"Attr9": 4
},
{
"Attr7": "string4",
"Attr8": "string5",
"Attr9": 5
}
],
"Attr10": 6,
"Attr14": {
"Attr10": "string6",
"Attr11": "string7",
"Attr12": "string8"
},
"Attr13": [
"string9",
123.45,
false
],
"Attr15": "string11"
}]
}
""";
}
After having taken a cue from Kolban's answer in this post Convert a JSON String to a HashMap I should have found a solution:
public class JsonUtils
{
public static JsonObject replaceValue( final JsonObject jsonObject, final String jsonKey, final Object jsonValue )
{
JsonObjectBuilder builder = Json.createObjectBuilder();
if (jsonObject != JsonObject.NULL)
{
builder = replace(jsonObject, jsonKey, jsonValue, builder);
}
return builder.build();
}
private static JsonObjectBuilder replace( final JsonObject jsonObject, final String jsonKey, final Object jsonValue, final JsonObjectBuilder builder )
{
Iterator<Entry<String, JsonValue>> it = jsonObject.entrySet().iterator();
while (it.hasNext())
{
#SuppressWarnings( "rawtypes" )
JsonObject.Entry mapEntry = it.next();
String key = mapEntry.getKey().toString();
Object value = mapEntry.getValue();
if (key.equalsIgnoreCase(jsonKey))
{
if (jsonValue instanceof String)
{
builder.add(jsonKey, (String) jsonValue);
}
else
{
builder.add(jsonKey, (JsonValue) jsonValue);
}
// here you can add the missing casting you need
continue;
}
if (value instanceof JsonArray)
{
value = toJsonArray((JsonArray) value, jsonKey, jsonValue, builder);
}
else if (value instanceof JsonObject)
{
JsonObjectBuilder newBuilder = Json.createObjectBuilder();
value = replace((JsonObject) value, jsonKey, jsonValue, newBuilder);
if (value instanceof JsonObjectBuilder)
{
value = ((JsonObjectBuilder) value).build();
}
}
builder.add(key, (JsonValue) value);
}
return builder;
}
private static JsonArray toJsonArray( final JsonArray array, final String jsonKey, final Object jsonValue, final JsonObjectBuilder builder )
{
JsonArrayBuilder jArray = Json.createArrayBuilder();
for (int i = 0; i < array.size(); i++)
{
Object value = array.get(i);
if (value instanceof JsonArray)
{
value = toJsonArray((JsonArray) value, jsonKey, jsonValue, builder);
}
else if (value instanceof JsonObject)
{
JsonObjectBuilder newBuilder = Json.createObjectBuilder();
value = replace((JsonObject) value, jsonKey, jsonValue, newBuilder);
if (value instanceof JsonObjectBuilder)
{
value = ((JsonObjectBuilder) value).build();
}
}
jArray.add((JsonValue) value);
}
return jArray.build();
}
Just keep in mind that this works if the key you want to replace is unique in the whole JsonObject.
Any improvement is more than appreciated...
If you don't want to use the streaming API (as used in my other answer), I think you can achieve a more compact approach - which is similar to yours - using JsonObjectBuilder and JsonOArrayBuilder, together with recursion:
private static JsonStructure iterate(final JsonStructure json) {
if (json.getValueType().equals(ValueType.OBJECT)) {
JsonObjectBuilder builder = Json.createObjectBuilder();
json.asJsonObject().forEach((key, value) -> {
switch (value.getValueType()) {
case OBJECT:
if (key.equals(targetKey)) {
builder.add(key, replacementJson);
} else {
builder.add(key, iterate(value.asJsonObject()));
} break;
case ARRAY:
if (key.equals(targetKey)) {
builder.add(key, replacementJson);
} else {
builder.add(key, iterate(value.asJsonArray()));
} break;
default:
if (key.equals(targetKey)) {
builder.add(key, replacementJson);
} else {
builder.add(key, value);
} break;
}
});
return builder.build();
} else if (json.getValueType().equals(ValueType.ARRAY)) {
JsonArrayBuilder builder = Json.createArrayBuilder();
json.asJsonArray().forEach((value) -> {
switch (value.getValueType()) {
case OBJECT:
builder.add(iterate(value.asJsonObject()));
break;
case ARRAY:
builder.add(iterate(value.asJsonArray()));
break;
default:
builder.add(value);
break;
}
});
return builder.build();
}
return null;
}
Personally, it's harder for me to read this recursive code than it is for me to read the streaming code in my other answer. But it certainly more concise.
It works by iterating down into the nested levels of each JSON object and array, and then builds a copy of the original data from the deepest nested levels outwards. When it finds the specified replacement key, it uses the related replacement JSON as the key's value.
The above method can be invoked as follows - which pretty-prints the end result:
final JsonStructure jsonOriginal = Json.createReader(new StringReader(JSONSTRING)).readObject();
final JsonStructure jsonCopy = iterate(jsonOriginal);
Map<String, Boolean> config = new HashMap<>();
config.put(JsonGenerator.PRETTY_PRINTING, true);
String jsonString;
JsonWriterFactory writerFactory = Json.createWriterFactory(config);
try ( Writer writer = new StringWriter()) {
writerFactory.createWriter(writer).write(jsonCopy);
jsonString = writer.toString();
}
System.out.println(jsonString);
For my replacement JSON I used this, showing some test data examples:
private final static String targetKey = "Attr6";
//private final static JsonStructure replacementJson = Json.createArrayBuilder()
// .add("newString1")
// .add("newString2").build();
private final static JsonStructure replacementJson = Json.createObjectBuilder()
.add("newkey1", "newString1")
.add("newkey2", "newString2").build();
So, using the same starting JSON as in my other answer, this code produces the following:
{
"Attr0": null,
"Attr1": true,
"Attr2": false,
"Attr3": 3,
"Attr4": [
"string1"
],
"Attr5": [
{
"Attr6": {
"newkey1": "newString1",
"newkey2": "newString2"
},
"Attr10": 6,
"Attr14": {
"Attr10": "string6",
"Attr11": "string7",
"Attr12": "string8"
},
"Attr13": [
"string9",
123.45,
false
],
"Attr15": "string11"
}
]
}
I'm sorting JSON objects in a JSON array by passing the JSON key.
My program is working only when the json value is String
Example:- {"id":"0001"}
so whenever there is integer value program throws an error.
Example:- {"id":0001}
program:-
import java.util.*;
import org.json.*;
public class sample2 {
public static void main(String[] args) {
String jsonArrStr = "[ "
+ ""
+ "{ \"ID\": \"135\", \"Name\": \"Fargo Chan\" },"
+ "{ \"ID\": \"432\", \"Name\": \"Aaron Luke\" },"
+ "{ \"ID\": \"252\", \"Name\": \"Dilip Singh\" }]";
JSONArray jsonArr = new JSONArray(jsonArrStr);
JSONArray sortedJsonArray = new JSONArray();
List<JSONObject> jsonValues = new ArrayList<JSONObject>();
for (int i = 0; i < jsonArr.length(); i++) {
jsonValues.add(jsonArr.getJSONObject(i));
}
Collections.sort(jsonValues, new Comparator<JSONObject>() {
// You can change "Name" with "ID" if you want to sort by ID
private static final String KEY_NAME = "Name";
#Override
public int compare(JSONObject a, JSONObject b) {
System.out.println("a " + a.toString());
System.out.println("b " + b.toString());
String valA = new String();
String valB = new String();
try {
valA = (String) a.get(KEY_NAME);
System.out.println("valA " + valA);
valB = (String) b.get(KEY_NAME);
System.out.println("valB " + valB);
} catch (JSONException e) {
// do something
}
return valA.compareTo(valB);
// if you want to change the sort order, simply use the following:
// return -valA.compareTo(valB);
}
});
for (int i = 0; i < jsonArr.length(); i++) {
sortedJsonArray.put(jsonValues.get(i));
}
System.out.println(sortedJsonArray.toString());
}
}
How to make above program work dynamically for all the DATA-TYPES(String,Integer,Float,Double).
There are not many ways to to what you want to achieve. Refelection API is there but it comes with many drawbacks and not very reliable and straightforward. However, the most reliable and easy way is to use instanceof operator.
With this method you have full control over every conversion and comparison, also you can compare custom classes with compareTo() method implemented. Unless you have too many possibilities to cover, this maybe the best approach.
I assume that you know that JSONObject.get() method converts object into Object types so add conditions like
Object obj1 = a.get(KEY_NAME);
Object obj2 = b.get(KEY_NAME);
if(obj1 instanceof Integer && obj2 instanceof Integer){
return ((Integer) obj1).compareTo(((Integer) obj2));
} else if(obj1 instanceof Double && obj2 instanceof Double){
return ((Double) obj1).compareTo(((Double) obj2));
} else if(obj1 instanceof Double || obj2 instanceof Double){
Double v1 = ((Number) obj1).doubleValue();
Double v2 = ((Number) obj2).doubleValue();
return v1.compareTo(v2);
}
As Number class is super class of Integer, Float and Double, so you
can convert it from there.
It may induce lot of code but will add reliability but no surprises when unexpexcted JSON is encountered. Also additional conditions would be able handle to handle mismatching data types and and could also detect error in JSON object.
Can anyone help me? How to convert the below Input to JSON Object?
Input :
{ "details": { "device/0/endPointClientName": "ndm-xx-1", "device/1/endPointClientName": "ndm-xx-2", "EnergyMeter/0/current": "20", "EnergyMeter/0/total": "400", } }
Output:-
{ "device": [ {"endPointClientName":"ndm-xx-1" }, {"endPointClientName":"ndm-xx-2" } ], "EnergyMeter": [ {"current":"20", "total":"400"} ] }
I have the Input as JSON Object with Properties class. In the Input we are sharing the FULL PATH. we have to convert this to JSON Object.
[demo]https://jsfiddle.net/CntChen/vh7kat5a/
var input = {
"details": {
"device/0/endPointClientName": "ndm-xx-1",
"device/1/endPointClientName": "ndm-xx-2",
"EnergyMeter/0/current": "20",
"EnergyMeter/0/total": "400",
}
};
function decodeFlatObj(flatOjb) {
var outputObj = {};
for (var key in flatOjb) {
var objNow = outputObj;
var subkey = key.split('/');
for (var i = 0; i < subkey.length - 1; i++) {
// next subkey is number
if (/\d|[1-9]\d*/.test(subkey[i + 1])) {
// current subkey is number
if (/\d|[1-9]\d*/.test(subkey[i])) {
objNow.push([]);
objNow = objNow[parseInt(subkey[i])];
} else {
objNow[subkey[i]] = objNow[subkey[i]] || [];
objNow = objNow[subkey[i]];
}
} else { // next subkey is object
// current subkey is number
if (/\d|[1-9]\d*/.test(subkey[i])) {
objNow[parseInt(subkey[i])] = objNow[parseInt(subkey[i])] || {};
objNow = objNow[parseInt(subkey[i])];
} else {
objNow[subkey[i]] = objNow[subkey[i]] || {};
objNow = objNow[subkey[i]];
}
}
}
var valueDecode;
if (typeof flatOjb[key] === 'object') {
valueDecode = decodeFlatObj(flatOjb[key]);
} else {
valueDecode = flatOjb[key];
}
if (/\d|[1-9]\d*/.test(subkey[subkey.length - 1])) {
objNow[parseInt(subkey[subkey.length - 1])].push(valueDecode);
} else {
objNow[subkey[subkey.length - 1]] = valueDecode;
}
}
return outputObj;
}
var output = decodeFlatObj(input);
console.log(input);
console.log(JSON.stringify(output));
//{"details":{"device":[{"endPointClientName":"ndm-xx-1"},{"endPointClientName":"ndm-xx-2"}],"EnergyMeter":[{"current":"20","total":"400"}]}}
In my below code, colData stores JSON String. Sample example for colData-
{"lv":[{"v":{"price":70.0,"userId":419},"cn":3},
{"v":{"price":149.99,"userId":419},"cn":3},
{"v":{"price":54.95,"userId":419},"cn":3}],
"lmd":20130206212543}
Now I am trying to match id value with userId value in the above JSON String. I am getting id value from a different source.
Meaning if id value is 419 then in the above JSON String userId value should also be 419. And in the JSON String, it might be possible there are lot of userId values so all the userId values should be matching with id. If any of them doesn't matches then log the exception.
So I was trying something like this-
final int id = generateRandomId(random);
for (String str : colData) {
if (!isJSONValid(str, id)) {
// log the exception here
LOG.error("Invalid JSON String " +str+ "with id" +id);
}
}
public boolean isJSONValid(final String str, final int id) {
boolean valid = false;
try {
final JSONObject obj = new JSONObject(str);
final JSONArray geodata = obj.getJSONArray("lv");
final int n = geodata.length();
for (int i = 0; i < n; ++i) {
final JSONObject person = geodata.getJSONObject(i);
JSONObject menu = person.getJSONObject("v");
if(menu.getInt("userId") == id) {
valid = true;
}
}
} catch (JSONException ex) {
valid = false;
}
return valid;
}
As per my understanding it looks like I can make isJSONValid method more cleaner. In my above isJSONValid method as I am repeating some stuff which I shouldn't be doing. Can anyone help me out how to make this more cleaner if I have missed anything. I will be able to learn some more stuff. Thanks for the help
You can initialize valid = true and set it to false when you find a non-valid userId and immediately fail:
public boolean isJSONValid(final String str, final int id) {
boolean valid = true;
try {
final JSONObject obj = new JSONObject(str);
final JSONArray geodata = obj.getJSONArray("lv");
final int n = geodata.length();
for (int i = 0; i < n; ++i) {
final JSONObject person = geodata.getJSONObject(i);
JSONObject menu = person.getJSONObject("v");
if(menu.getInt("userId") != id) {
valid = false;
break;
}
}
} catch (JSONException ex) {
valid = false;
}
return valid;
}
This way you iterate through all array's elements only if all are valid, which is the only case you actually have to.
I would like to know a method for flagging values in an array, removing the duplicates and combining some of the data in Java.
I am keeping a record of geo locations using lat, long and description this is encoded in a JSON array as follows:
[{"lon": 0.001, "lat": 0.001, "desc": test}, {"lon": 0.001, "lat": 0.001, "desc": test2}]
I would like to be able to remove the duplicate geo locations while keeping the "desc" part of the array, e.g.
[{"lon": 0.001, "lat": 0.001, "desc": test, test2}]
Edit:
This is what I am currently doing:
//Store locPoints from server in JSONArray
JSONArray jPointsArray = new JSONArray(serverData);
List<JSONObject> jObjects = new ArrayList<JSONObject>();
List<JSONObject> seenObjects = new ArrayList<JSONObject>();
for(int i = 0; i < jPointsArray.length(); ++i)
{
jObjects.add(jPointsArray.getJSONObject(i));
}
for (JSONObject obj : jObjects)
{
//This always returns true
if (!seenObjects.contains(obj))// && !seenObjects.contains(obj.get("lon")))
{
Log.i("Sucess", "Huzzah!");
seenObjects.add(obj);
}
else
{
//merge the 'desc' field in 'obj' with the 'desc' field in
JSONObject original = (JSONObject)seenObjects.get(seenObjects.indexOf(obj));
JSONObject update = obj;
original.put("desc", original.get("desc") + ", " + update.get("desc"));
seenObjects.get(seenObjects.indexOf(obj)).get("desc"));
}
}
You could do something like:
//assuming that the array you are filtering is called 'myArray'
List<Object> seenObjects = new ArrayList<Object>();
for (Object obj : myArray) {
if (! seenObjects.contains(obj)) {
seenObjects.add(obj);
}
else {
//merge the 'desc' field in 'obj' with the 'desc' field in
//'seenObjects.get(seenObjects.indexOf(obj))'
}
}
Note that this will only work if the objects you are comparing have implementations of equals() and hashCode() that do what you want (in your case, they should only take into consideration the 'lat' and 'lon' fields).
Update:
Here is some complete example code:
import java.util.ArrayList;
import java.util.List;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
public class JsonMergeTest {
#SuppressWarnings({ "rawtypes", "unchecked" })
public static void main(String[] args) {
List<Object> myArray = new ArrayList<Object>();
myArray.add(MyJsonObject.parse("{\"lon\": 0.001, \"lat\": 0.001, \"desc\": \"test\"}"));
myArray.add(MyJsonObject.parse("{\"lon\": 0.001, \"lat\": 0.001, \"desc\": \"test2\"}"));
List seenObjects = new ArrayList<Object>();
for (Object obj : myArray) {
if (! seenObjects.contains(obj)) {
seenObjects.add(obj);
}
else {
//merge the 'desc' field in 'obj' with the 'desc' field in the list
MyJsonObject original = (MyJsonObject)seenObjects.get(seenObjects.indexOf(obj));
MyJsonObject update = (MyJsonObject)obj;
original.put("desc", original.get("desc") + ", " + update.get("desc"));
}
}
for (MyJsonObject obj : (List<MyJsonObject>)seenObjects) {
System.out.println(obj.toJSONString());
}
}
private static class MyJsonObject extends JSONObject {
#Override
public boolean equals(Object obj) {
if (obj == null || ! (obj instanceof MyJsonObject) || ! this.containsKey("lat") || ! this.containsKey("lon")) {
return super.equals(obj);
}
MyJsonObject jsonObj = (MyJsonObject)obj;
return this.get("lat").equals(jsonObj.get("lat")) && this.get("lon").equals(jsonObj.get("lon"));
}
#Override
public int hashCode() {
if (! this.containsKey("lat") || ! this.containsKey("lon")) {
return super.hashCode();
}
return this.get("lat").hashCode() ^ this.get("lon").hashCode();
}
#SuppressWarnings("unchecked")
public static Object parse(String json) {
Object parsedJson = JSONValue.parse(json);
if (! (parsedJson instanceof JSONObject)) {
return parsedJson;
}
MyJsonObject result = new MyJsonObject();
result.putAll((JSONObject)parsedJson);
return result;
}
}
}
You can use GSon. And follow the steps:
1. Define an equivalent POJO in Java, to map the JSON String
public class Location implements Comparable<Location> {
public String lon;
public String lat;
public String desc;
#Override
public String toString() {
return "<lon: " + lon +", lat: "+ lat +", desc: " + desc +">";
}
#Override
public boolean equals(Object obj) {
return ((Location)obj).lon.equals(lon) && ((Location)obj).lat.equals(lat);
}
public int compareTo(Location obj) {
return ((Location)obj).lon.compareTo(lon) + ((Location)obj).lat.compareTo(lat);
}
}
2. Write the code that merges similar location. OK, it's Sunday, lets do it :)
public static void main(String[] args){
//Some test data
String s = "[" +
" {\"lon\": 0.001, \"lat\": 0.001, \"desc\": \"test\"}," +
" {\"lon\": 0.002, \"lat\": 0.001, \"desc\": \"test3\"}," +
" {\"lon\": 0.002, \"lat\": 0.005, \"desc\": \"test4\"}," +
" {\"lon\": 0.002, \"lat\": 0.001, \"desc\": \"test5\"}," +
" {\"lon\": 0.001, \"lat\": 0.001, \"desc\": \"test2\"}]";
Gson gson = new Gson();
Location[] al = gson.fromJson(s, Location[].class);
List<Location> tl = Arrays.asList(al);
//lets sort so that similar locations are grouped
Collections.sort(tl);
List<Location> fl = new ArrayList<Location>();
Location current = null;
//merge!
for(Iterator<Location> it = tl.iterator(); it.hasNext();){
current = current==null?it.next():current;
Location ltmp = null;
while(it.hasNext() && (ltmp = it.next()).equals(current))
current.desc = current.desc + "," + ltmp.desc;
fl.add(current);
current = ltmp;
}
//convert back to JSON?
System.out.println(gson.toJson(fl));
}
3. output
[{"lon":"0.002","lat":"0.005","desc":"test4"},
{"lon":"0.002","lat":"0.001","desc":"test3,test5"},
{"lon":"0.001","lat":"0.001","desc":"test,test2"}]