I have a JSONArray as below,
JSONArray dataArray = new JSONArray();
dataArray = [
{
"name": "name1",
"row": 1,
"value": 20
},
{
"name": "name2",
"row": 1,
"value": 10
},
{
"name": "name3",
"row": 2,
"value": 10
},
{
"name": "name4",
"row": 3,
"value": 30
},
{
"name": "name5",
"row": 3,
"value": 10
}
]
I need to compare the row attribute, if same, need to compare value attribute and sort the object in the array.
Tried with Java comparator, but couldn't make it work. Can somebody please help?
for(int i = 0; i < dataArray.size(); i++) {
elementList.add((JSONObject) dataArray.get(i));
}
Long row1 = null;
for (JSONObject obj : elementList) {
if(row1 == null) {
row1 = (Long) ((JSONObject) obj.get("row"));
}
else {
Long row2 = (Long) ((JSONObject) obj.get("row"));
if(row2 == row1) {
//call the comparator, but how to pass two objects?
}
row1 = row2;
}
}
It would be easy to extend this answer to match your scenario
But instead of
return valA.compareTo(valB);
you should do
int comp = valA.compareTo(valB);
if (comp == 0){
String valC = (String) a.get(KEY_NAME2);
String valD = (String) b.get(KEY_NAME2);
return valC.compareTo(valD);
} else {
return comp;
}
So it should be the following.
JSONArray sortedJsonArray = new JSONArray();
List<JSONObject> jsonValues = new ArrayList<JSONObject>();
for (int i = 0; i < dataArray.length(); i++) { // <---- dataArray is the input that you have
jsonValues.add(dataArray.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_NAME1 = "row";
private static final String KEY_NAME2 = "value";
#Override
public int compare(JSONObject a, JSONObject b) {
String valA = new String();
String valB = new String();
try {
valA = (String) a.get(KEY_NAME1);
valB = (String) b.get(KEY_NAME1);
}
catch (JSONException e) {
//do something
}
int comp = valA.compareTo(valB);
if (comp == 0){
String valC = (String) a.get(KEY_NAME2);
String valD = (String) b.get(KEY_NAME2);
return valC.compareTo(valD);
} else {
return comp;
}
}
});
Edit: changed into KEY_NAME1 = "row"; to match the new question requirement
A simpler approach here:
You could convert your JSON string to List<YourObject> by using Jackson's ObjectMapper
List<YourObject> list = objectMapper.readValue(json, new TypeReference<List<YourObject>>(){});
Then use Collections.sort and Comparator to sort this list. You can also implement a custom Comparator to sort a list by multiple attributes depending on your situation.
list.sort(Comparator.comparing(YourObject::getRow)
.thenComparing(YourObject::getValue));
Let's assume you deserialize your json data into objects like
public class DataRow {
private String name;
private int row;
private int value;
// Getter, Setter, Constructor what ever needed
}
Then you can work with a list and stream like:
List<DataRow> datarows = //... read data from json
List<DataRow> sorted = datarows.stream()
.sorted(Comparator.comparing(DataRow::getRow).thenComparingInt(DataRow::getValue)).toList();
Related
I have two JSON strings which I want to compare.
I want neither the order of the keys to matter or the order of elements in an array.
However I do want an extra field to be considered "not equal"
Non strict mode with JSONAssert seems like it fits the bill except for an extra field being considered equal "http://jsonassert.skyscreamer.org/cookbook.html"
If at all possible I would like to avoid pulling in extra dependancies. I already have jackson in my project
I have 2 ideas how to do it.
Is to write java objects and serialize it, and write own equals method.
Is to serialize it to Map<Object, Object> and compare 2 map.
String json1 = "{...}"
String json2= "{...}"
Object json1Object = objectMapper.readValue(json1, Object.class);
Object json2Object = objectMapper.readValue(json2, Object.class);
Assertions.assertEquals(json1Object, json2Object);
Assertions.assertTrue(json1Object.equals(json2Object));
So you probably have only one option. Write own comparator.
My quick solution:
#Test
public void comparingJsonTest4() throws JsonProcessingException {
String json1 = "{\"id\": 1, \"name\": \"test\", \"cars\": [\"Ford\", \"BMW\", \"Fiat\"]}";
String json2 = "{\"name\": \"test\", \"id\": 1, \"cars\": [\"BMW\", \"Ford\", \"Fiat\"]}";
ObjectMapper objectMapper = new ObjectMapper();
JsonNode json1Node = objectMapper.readTree(json1);
JsonNode json2Node = objectMapper.readTree(json2);
Assertions.assertEquals(0, new ComparatorWithoutOrder().compare(json1Node, json2Node));
}
class ComparatorWithoutOrder implements Comparator<JsonNode> {
#Override
public int compare(JsonNode o1, JsonNode o2) {
if(o1 == o2) {
return 0;
}
if(o1.getClass() != o2.getClass()) {
return -1;
}
if(o1.getClass() == ObjectNode.class) {
List<String> o1FieldNames = new ArrayList<>();
o1.fieldNames().forEachRemaining(o1FieldNames::add);
List<String> o2FieldNames = new ArrayList<>();
o2.fieldNames().forEachRemaining(o2FieldNames::add);
if(o1FieldNames.size() != o2FieldNames.size()) {
return -1;
}
if(!o2FieldNames.containsAll(o1FieldNames) || !o1FieldNames.containsAll(o2FieldNames)) {
return -1;
}
for (String o1FieldName : o1FieldNames) {
if (!(compare(o1.get(o1FieldName), o2.get(o1FieldName)) == 0)) {
return -1;
}
}
return 0;
}
if(o1.getClass() == ArrayNode.class) {
List<JsonNode> o1Children = new ArrayList<>();
o1.elements().forEachRemaining(o1Children::add);
List<JsonNode> o2Children = new ArrayList<>();
o2.elements().forEachRemaining(o2Children::add);
if(o1Children.size() != o2Children.size()) {
return -1;
}
for (JsonNode c1 : o1Children) {
boolean found = false;
for (JsonNode c2 : o2Children) {
if (compare(c1, c2) == 0) {
found = true;
break;
}
}
if (!found) {
return -1;
}
}
return 0;
}
return o1.equals(o2) ? 0 : -1;
}
}
At the beginning I wanted to use something like this:
json1Node.equals(new ComparatorWithoutOrder(), json2Node);
but thre was a problem to propoer handle ArrayNode inside ObjectNode. So if you want, you could skip implements Comparator<JsonNode>, because finally I don't use this functionality.
I have to iterate through a List of Mapper and maper is a list of strings, my json looks like:
{
"aa":{
"val":"",
"agl":
[
{
"a": "A1",
"b": "B1",
"c": "C1"
},
{
"a": "A2",
"b": "B2",
"c": "C2"
}
]
}
}
The result is to get a,b and c values with iterator mode.
I tried this the code below but i have the same error JSONArray[0] is not a JSONObject
List<Mapper> agl= fileDescription.get("aa").getAgl();
JSONArray jsonArray = new JSONArray(agl);
System.out.println(jsonArray);
for (int i = 0; i < jsonArray.length(); i++) {
Object jsonObj = jsonArray.getJSONObject(i);
System.out.println(jsonObj);
}
I tried also this code but it don't give me the result i need:
Iterator iterator = agl.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
System.out.println( iterator1.getA());
Mapper.java
public class Mapper{
#JsonProperty("a")
private String a;
#JsonProperty("b")
private String b;
#JsonProperty("c")
private String c;
public String getA() {
return a;
}
public void setA(String a) {
this.a= a;
}
public String getB() {
return b;
}
public void setB(String b) {
this.b= b;
}
public String getC() {
return c;
}
public void setC(String c) {
this.c= c;
}
#Override
public String toString() {
return "{\"a\":" + "\""+a+ "\"" + ", \"b\":" + "\"" + b + "\"" + ", \"c\":" + "\"" + c + "\"" + "}";
}
}
I think you're misusing the type system. You already have a valid list to iterate. Don't create another one
List<Mapper> agl= fileDescription.get("aa").getAgl();
Iterator<Mapper> iterator = agl.iterator();
while (iterator.hasNext()) {
Mapper m = iterator.next();
System.out.println(m.getA());
}
Or simply
List<Mapper> agl= fileDescription.get("aa").getAgl();
for (Mapper m : agl) {
System.out.println(m.getA());
}
Or agl.forEach(m -> System.out.println(m.getA()));
so i asked before but it seems i wasnt clear enough of what im talking about, so im trying to make it clearer now:
what im trying to do is prepare data for an import. the data i get is human made an not very efficient, so im removing unnecessary entrys and try to combine the data as much as possible.
its for something like a configurator. the data i get looks something like this:
123 : 45 : AB = 12
This means: if Option 1 is 1 OR 2 OR 3 and Option 2 is 4 OR 5 and Option 3 is A OR B the result will be 1 AND 2
i created a class thats something like this:
Class Options{
String opt1;
String opt2;
String opt3;
String optResult;
//and some other stuff
boolean hasSameOptions(Options o){
return opt1.equals(o.opt1) && opt2.equals(o.opt2) && opt3.equals(o.opt3);
}
public void AddOptions(String options) {
for (String s : options.split("")) {
if (!optResult.contains(s)) {
optResult = optResult + s;
}
}
}
}
now, the data is repetitive and can be combined. Like:
12 : 45 : AB = 12
12 : 45 : AB = 3
12 : 45 : AB = 4
This would mean actually mean: 12 : 45 : AB = 1234
So, what i do is break the Strings apart to get only single values with the result, for example:
1 : 4 : A = 12
1 : 4 : B = 12
1 : 5 : A = 12
//and so on.
I make a list of all these Values and then try to Combine them again to get more efficient List.
The first step i do is get all Objects who have the same Options but different Results and combine the results. that happens like this:
public static List<Options> cleanList(List<Options> oldList) {
List<Options> newList = new ArrayList<>();
for (Options item : oldList) {
Options temp = findEqualOptions(newList, item);
if (temp != null)
temp.AddOptions(item.optResult);
else
newList.add(item);
}
return newList;
}
public static <T> T findByProperty(Collection<T> col, Predicate<T> filter) {
return col.stream().filter(Objects::nonNull).filter(filter).findFirst().orElse(null);
}
public static Options findEqualOptions(List<Options> list, Options opt) {
return findByProperty(list, d -> d.hasSameOptions(opt));
}
After that, i try to compress the list even more, by combining elements who have only ONE different value. For example:
1 : 2 : A = 12
1 : 3 : A = 12
-> 1 : 23 : A = 12
i do it like this:
for (int i = 0; i < list.size(); i++) {
for (int j = i + 1; j < list.size(); j++) {
Option o1 = list.get(i);
Option o2 = list.get(j);
int diff1 = 0;
int diff2 = 0;
int diff3 = 0;
int diff4 = 0;
if(!o1.opt1.equals(o2.opt1))
diff1 = 1;
if(!o1.opt2.equals(o2.opt2))
diff2 = 1;
//and so on
if((diff1+diff2+diff3+diff4)>1)
continue;
if(diff1 == 1)
o1.opt1 = o1.opt1 + o2.opt1;
//and so on...
list.remove(j--);
}
}
i do this until there are no more changes. It works well, but slowly. especially the method cleanList().
does anybody have any idea how to make it better? i tried to use a stream to get the whole list of equals options directly like this:
public static <T> List<T> findByMultipleValue(Collection<T> col, Predicate<T> filter) {
return col.stream().filter(filter).collect(Collectors.toList());
}
public static List<Options> getEqualOptionsList(List<Options> optList, Options opt){
return findByMultipleValue(optList, o -> o.hasSameOptions(opt));
}
but that made it A LOT slower.
PS. : its not the complete code, just an example of what im trying to do. I hope it is more understandable this time :)
probably not the most elegant or optimal solution but here is already a quick approach that give the result based on your description. It use the HashMap as proposed in the comment of #Joseph Larson
I went for a set of char to ensure values are not duplicate in it but feel free to adapt :)
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
class Scratch {
public static class Option{
String opt1;
String opt2;
String opt3;
String optResult;
public Option(String opt1, String opt2, String opt3, String optResult) {
this.opt1 = opt1;
this.opt2 = opt2;
this.opt3 = opt3;
this.optResult = optResult;
}
public static String merge(String a, String b){
StringBuilder value = new StringBuilder();
Set<Character> result = new HashSet<>();
for(char c : a.toCharArray()){
result.add(c);
}
for(char c : b.toCharArray()){
result.add(c);
}
for(char c : result){
value.append(c);
}
return value.toString();
}
public Option(Option a, Option b) {
this(merge(a.opt1, b.opt1), merge(a.opt2, b.opt2), merge(a.opt3, b.opt3), merge(a.optResult, b.optResult));
}
String getKey(){
return String.join(":", opt1, opt2, opt3);
}
int distance(Option option){
int diff1 = this.opt1.equals(option.opt1)?0:1;
int diff2 = this.opt2.equals(option.opt2)?0:1;
int diff3 = this.opt3.equals(option.opt3)?0:1;
int diff4 = this.optResult.equals(option.optResult)?0:1;
return diff1 + diff2 + diff3 + diff4;
}
public String toString(){
return getKey();
}
}
public static void main(String[] args) {
Option[] data = new Option[]{
new Option("12", "45", "AB", "12"),
new Option("12", "45", "AB", "3"),
new Option("12", "45", "AB", "4"),
new Option("12", "45", "AC", "1"),
new Option("12", "45", "AC", "12"),
new Option("3", "45", "AC", "13"),
new Option("12", "45", "AD", "12"),
};
mergeExact(data);
mergeClose(data, 1);
}
private static void mergeClose(Scratch.Option[] data, int distance){
Map<Option, Set<Character>> buffer = new HashMap<>();
for(Option option : data) {
boolean found = false;
Option toDelete = null;
for(Map.Entry<Option, Set<Character>> entry : buffer.entrySet()){
if(option.distance(entry.getKey()) <= distance){
Option merged = new Option(entry.getKey(), option);
for(char c : option.optResult.toCharArray()){
entry.getValue().add(c);
}
buffer.put(merged, entry.getValue());
toDelete = entry.getKey();
found = true;
break;
}
}
if(found) {
buffer.remove(toDelete);
}else{
Set<Character> set = new HashSet<>();
for(char c : option.optResult.toCharArray()){
set.add(c);
}
buffer.put(option, set);
}
}
System.out.println(String.format("merge with distance of %d:: %s", distance, buffer));
}
private static void mergeExact(Scratch.Option[] data) {
Map<String, Set<Character>> buffer = new HashMap<>();
for(Option option : data){
Set<Character> item = buffer.computeIfAbsent(option.getKey(), k -> new HashSet<>());
for(char c : option.optResult.toCharArray()){
item.add(c);
}
}
System.out.println("exact merge:: "+buffer);
}
}
output is
exact merge:: {3:45:AC=[1, 3], 12:45:AD=[1, 2], 12:45:AC=[1, 2], 12:45:AB=[1, 2, 3, 4]}
merge with distance of 1:: {12:45:AB=[1, 2, 3, 4], 3:45:AC=[1, 3], 12:45:ACD=[1, 2]}
EDIT: missed a part of the question, updating to add the merge when difference is close. This part is probably even worst that the first one in terms of optimisation but it's a working bases :)
I have a Map with a key and value as linked List as shown below
{Ice creams=[Cone,KoolCool(21), Stick,KoolCool(25)]}
With this i need to construct a following json
{
"name": "Ice creams",
"T2": [
{
"name": "Cone",
"T3": [
{
"name": "KoolCool",
"leaf": []
}
]
},
{
"name": "Stick",
"T3": [
{
"name": "KoolCool",
"leaf": []
}
]
}
]
}
Every comma seperated value will increment the T value
I am trying to apprach this with the below
For every key in the Map Access its value as LinkedList
From that LinkedList access split it and construct the json
Could anybody please let me know if there is a better way of doing this ??
package com.util;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
public class Test {
public static void main(String args[]) {
Map<String, LinkedList<String>> consilatedMapMap = new LinkedHashMap<String, LinkedList<String>>();
LinkedList<String> values = new LinkedList<String>();
values.add("Cone,KoolCool(21)");
values.add("Stick,KoolCool(25)");
consilatedMapMap.put("Ice creams", values);
/*
* for (Map.Entry<String, LinkedList<String>> consilMap :
* consilatedMapMap.entrySet()) {
*
* String t1Category = consilMap.getKey(); LinkedList<String>
* consiladatedList = consilMap.getValue();
*
*
* for(int i=0;i<consiladatedList.size();i++) { String result =
* consiladatedList.get(i);
*
* String spliter[] = result.split(",");
*
* for(int j=0;j<spliter.length;j++) { System.out.println(spliter[j]); }
*
* }
*
* }
*/
}
}
I believe you're overcomplicating this - autoincremented names... The JSON structure doesn't match the Java structure... JSON generated partly by object structure, partly by parsing strings within it... even though you say "Every comma seperated value will increment the T value" this is obviously not true from the example.
Nevertheless... this is possible - even just using org.json (not sure why people keep suggesting GSON as a fix all to this...)
The primary idea here is to make a method for each part you need to generate, and pass the "level" around to generate the appropriate "Tn" property when you need it.
public class Test {
private static JSONObject processString(String data, int level) throws JSONException {
JSONObject json = new JSONObject();
int index = data.indexOf(',');
String name = data;
String remainder = "";
if (index < 0) {
index = name.indexOf('(');
if (index > 0) {
name = data.substring(0, index);
}
} else {
name = data.substring(0, index);
remainder = data.substring(name.length() + 1);
}
json.put("name", name);
JSONArray a = new JSONArray();
if (remainder.length() > 0) {
a.put(processString(remainder, level + 1));
json.put("T" + level, a);
} else {
json.put("leaf", a);
}
return json;
}
private static JSONArray processList(List<String> list, int level) throws JSONException {
JSONArray json = new JSONArray();
for (String data : list) {
json.put(processString(data, level));
}
return json;
}
private static JSONObject processMap(Map<String>, List<String>> map, int level) throws JSONException {
JSONObject json = new JSONObject();
for (String key : map.keySet()) {
json.put("name", key);
json.put("T" + level, processList(map.get(key), level + 1));
}
return json;
}
public static void main(String args[]) {
Map<String, List<String>> consilatedMapMap = new LinkedHashMap<String, List<String>>();
List<String> values = new LinkedList<String>();
values.add("Cone,KoolCool(21)");
values.add("Stick,KoolCool(25)");
consilatedMapMap.put("Ice creams", values);
try {
int level = 2;
JSONObject json = processMap(consilatedMapMap, level);
} catch(JSONException x) {
x.printStackTrace();
System.exit(-1);
}
}
There is a utility library called Gson on Google code.You can check it out here
For some simple examples visit this
Look at folowing code:
Map<String, LinkedList<String>> consilatedMapMap = new LinkedHashMap<String, LinkedList<String>>();
LinkedList<String> values = new LinkedList<String>();
values.add("Cone,KoolCool(21)");
values.add("Stick,KoolCool(25)");
consilatedMapMap.put("Ice creams", values);
Gson gson=new Gson();
String jsonElement=gson.toJson(consilatedMapMap);
System.out.println(jsonElement);
Output
{"Ice creams":["Cone,KoolCool(21)","Stick,KoolCool(25)"]}
As you can see from the code and it's output, Gson library would convert your data structure to a json string.
All you need is this Gson library.
I have an array of objects of my class "Points" and I want to put them on a JSONArray, previusly cast to JSONObject, but I have one problem I can't solve, I can't put my points[] on the JSONObject and I don't know how to do it. I put the code to explain it better.
Principal code:
JSONArray jsPoints = new JSONArray();
for(int i = 0; i < points.length; i++)
{
JSONObject js = new JSONObject(points[i]);
jsPoints.put(js);
}
Point class:
public class Point {
String _id;
String _comment;
String _calification;
String _coords;
int _X;
int _Y;
public Point(String id, String comment, String calification, String coords, int x, int y)
{
_id = id;
_comment = comment;
_calification = calification;
_coords = coords;
_X = x;
_Y = y;
}
}
Introducing values into my class:
private void createPoints()
{
points = new Point[drawedInterestPoints.size() / 2];
int j = 0;
for(int i = 0; i < drawedInterestPoints.size() - 1; i++)
{
if(i % 2 == 0)
{
points[j] = new Point(pointType.get(i),comments.get(i),calification.get(i),GPScoords.get(i),drawedInterestPoints.get(i),drawedInterestPoints.get(i + 1));
j++;
}
}
}
Anyone can tall me what I have to do to put each of my points[] in a JSONObject and then in a JSONArray? Thanks!
Try with GSON. Do this:
Gson gson = new Gson();
final MyClass myClass = gson.fromJson(jsonString, MyClass.class);
Hope this helps.. :)
you can not directly cast JSONArray to Points
what you can do something like following
get json as a string
de-serialize json to Point using jackson(ObjectMapper)
In your code
JSONObject js = new JSONObject(points[i]);
make no sense; You should put every items of points[] as tag in the jsObject and then insert it in a JsonArray
Try this:
public void jsonAsStr() {
JSONObject mJsonObj = new JSONObject();
try {
mJsonObj.put("tag1", true);
mJsonObj.put("tag2", 120);
mJsonObj.put("tag3", "Demo");
JSONArray jsPoints = new JSONArray();
jsPoints.put(mJsonObj);
} catch (JSONException e) {
e.printStackTrace();
}
}
You can do this way using gson
Gson gson = new Gson();
Point point = new Point("", "", "", "", 1, 2);
String json = gson.toJson(point);