HOW to remove duplicate JSON Objects from JSON Array - java

I have got the JSON as below
{
"brands": [
{
"name": "ACC",
"quantity": "0",
"listedbrandID": 1,
"status": "0"
}
],
"others": [
{
"name": "dd",
"quantity": "55"
},
{
"name": "dd",
"quantity": "55"
}
]
}
How can i remove the duplicates from others JSON array
i have tried as following
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
String json = "{
"brands": [
{
"name": "ACC",
"quantity": "0",
"listedbrandID": 1,
"status": "0"
}
],
"others": [
{
"name": "dd",
"quantity": "55"
},
{
"name": "dd",
"quantity": "55"
}
]
}
";
JSONObject json_obj = new JSONObject(json);
JSONArray array = json_obj.getJSONArray("others");
HashMap<String, String> map = new HashMap<String, String>();
System.out.println(array.length());
for(int i=0;i<array.length();i++)
{
String name = array.getJSONObject(i).getString("name");
String quantity = array.getJSONObject(i).getString("quantity");
if(name!=null && !name.trim().equals(""))
{
map.put(name, quantity);
}
}
But no idea how to remove duplicate JSON other than which are present under Map only.

Create an object representing your Others. It will have a name and quantity property and it will also override the equals method, wherein, two Other objects are considered to be equal if they have the same name and quantity properties.
Once you have that, iterate over your JSON, create a new Other object and place it in a HashSet<Other>. The .equals will ensure that the HashSet will contain unique items, as per your definition of unique.

Related

group a list of map by value and merge as grouped lists by sorting

I would like to group a list of maps by value and insert(merge) as grouped lists in certain conditions. Group by a value "yr" and merge "tag" by the same "category" then sum up the "price" them together, then after order in descending order by "price"
List<Grocery> groceryList = new ArrayList<>();
Grocery grocery = new Grocery();
List<HahsMap<String, Object>> tagList = new ArrayList<>();
HashMap<String, Object> tagMap = new HashMap<>();
## feeding data example
tagMap.put("category", "A");
tagMap.put("name", "Apple");
tagMap.put("price", 10);
tagList.add(tagMap);
grocery.setYr(String yr);
grocery.setTag(List<Hashmap<String, Object>> tagList);
groceryList.add(grocery);
This is an example of a list of maps.
List<Grocery> groceryList =
[
{
"yr": "2021",
"tag": [
{
"Category": "A",
"Name": "Apple",
"Price": 10
}
]
},
{
"yr": "2021",
"tag": [
{
"Category": "A",
"Name": "Apple",
"Price": 10
}
]
},
{
"yr": "2021",
"tag": [
{
"Category": "B",
"Name": "Banana",
"Price": 5
}
]
},
{
"yr": "2020",
"tag": [
{
"Category": "A",
"Name": "Apple",
"Price": 10
}
]
},
{
"yr": "2020",
"tag": [
{
"Category": "B",
"Name": "Banana",
"Price": 30
}
]
},
{
"yr": "2020",
"tag": [
{
"Category": "C",
"Name": "Candy",
"Price": 10
}
]
},
{
"yr": "2020",
"tag": [
{
"Category": "C",
"Name": "Candy",
"Price": 30
}
]
},
{
"yr": "2020",
"tag": [
{
"Category": "A",
"Name": "Apple",
"Price": 10
}
]
}
]
This is the sample output I want to get
List<Grocery> result =
[
{
"yr": "2021",
"tag": [
{
"Category": "A",
"Name": "Apple",
"Price": 20
},
{
"Category": "B",
"Name": "Banada",
"Price": 5
}
]
},
{
"yr": "2020",
"tag": [
{
"Category": "C",
"Name": "Candy",
"Price": 40
},
{
"Category": "B",
"Name": "Banada",
"Price": 30
},
{
"Category": "A",
"Name": "Apple",
"Price": 20
}
]
}
]
I know this is tricky to combine all the condition into one, could anyone help me out??
It will be really appreciated if at least I could have some ideas what to do about this..
I tried to group them in a 'yr' first, and try to find the values to map out then sort them out with the same 'category' then sum up.. but i have no clue on sorting them and merge them together with summed up 'price'..
Thanks in advance!!
Please check If this is what you are looking for.
String inputJson = "[{\"yr\":\"2021\",\"tag\":[{\"Category\":\"A\",\"Name\":\"Apple\",\"Price\":10}]},{\"yr\":\"2021\",\"tag\":[{\"Category\":\"A\",\"Name\":\"Apple\",\"Price\":10}]},{\"yr\":\"2021\",\"tag\":[{\"Category\":\"B\",\"Name\":\"Banana\",\"Price\":5}]},{\"yr\":\"2020\",\"tag\":[{\"Category\":\"A\",\"Name\":\"Apple\",\"Price\":10}]},{\"yr\":\"2020\",\"tag\":[{\"Category\":\"B\",\"Name\":\"Banana\",\"Price\":30}]},{\"yr\":\"2020\",\"tag\":[{\"Category\":\"C\",\"Name\":\"Candy\",\"Price\":10}]},{\"yr\":\"2020\",\"tag\":[{\"Category\":\"C\",\"Name\":\"Candy\",\"Price\":30}]},{\"yr\":\"2020\",\"tag\":[{\"Category\":\"A\",\"Name\":\"Apple\",\"Price\":10}]}]";
ObjectMapper objectMapper = new ObjectMapper();
//convert json to input Grocery List using jackson object mapper
List<Grocery> groceries = objectMapper.readValue(inputJson, new TypeReference<List<Grocery>>() {
});
List<Grocery> output = new ArrayList<>();
//group all groceries by year
Map<String, List<Grocery>> groceriesByYear = groceries.stream().collect(Collectors.groupingBy(grocery -> grocery.getYr()));
groceriesByYear.keySet().forEach(year -> {
//for each year, get the grocery list
List<Grocery> groceryList = groceriesByYear.get(year);
//merge tags which have same category
Map<String, Tag> tagsPerCategory = groceryList.stream().flatMap(grocery -> grocery.getTag().stream()).collect(Collectors.toMap(Tag::getCategory, Function.identity(),
(tag1, tag2) -> {
//merge function, sum up the price of tag which has same category
tag1.setPrice(tag1.getPrice() + tag2.getPrice());
return tag1;
}));
//create new grocery object with required data
Grocery grocery = new Grocery();
grocery.setYr(year);
grocery.setTag(new ArrayList<>(tagsPerCategory.values()));
output.add(grocery);
});
//output list will have groceries grouped by year and tag with summed up price
Edit
You can do filter on tag stream to avoid null tag objects and add not null check if you think price can be null or you can define a method to provide default value if price is not already set like below
public static <T> T getValueOrDefault(T value, T defaultValue) {
return value != null ? value : defaultValue;
}
and your tagsPerCategory looks like below
Map<String, Tag> tagsPerCategory = groceryList.stream().flatMap(grocery -> grocery.getTag().stream()).filter(tag -> tag != null && tag.getPrice() != null)
.collect(Collectors.toMap(Tag::getCategory, Function.identity(),
(tag1, tag2) -> {
//sum up the price of tag which has same category
tag1.setPrice(getValueOrDefault(tag1.getPrice(), 0) + getValueOrDefault(tag2.getPrice(), 0));
return tag1;
}));

How do I remove null values from LinkedHashMap<String,Object> with nested objects having null values

My JSON Doc is structured like this and being saved in MongoDB with null values.
{
"userId": "123456",
"session": "string",
"timestamp": 0,
"product": "tracker",
"version": "13",
"flight": "A",
"activity": "search",
"action": "opportunity",
"dataDictionary": {
"datadictionary": {
"query": "roofing",
"docid": 12,
"rank": 1,
"search": {
"id": null
}
},
"id": 40
}
I have also tried to put #JsonInclude(JsonInclude.Include.NON_NULL)
My Hash map is declared like
Map<String, Object >dataDict = new LinkedHashMap<>();
dataDict.values().removeIf(Objects::isNull);
As far as I can tell this should be removing all null values regardless of level/layer in the Map.
JSON is stored like this
{
"userId": "123456",
"session": "string",
"timestamp": 0,
"product": "tracker",
"version": "13",
"flight": "A",
"activity": "search",
"action": "opportunity",
"dataDictionary": {
"datadictionary": {
"query": "roofing",
"docid": 12,
"rank": 1,
"search": {
"id": null,
"name":"test"
}
},
"id": 40
}
Should be stored like this
{
"userId": "123456",
"session": "string",
"timestamp": 0,
"product": "tracker",
"version": "13",
"flight": "A",
"activity": "search",
"action": "opportunity",
"dataDictionary": {
"datadictionary": {
"query": "roofing",
"docid": 12,
"rank": 1,
"search": {
"name":"test"
}
},
"id": 40
}
The problem is that you are removing null values from the top level Map.
This map contains internally values that are other maps. You don't remove null values from thos maps.
Try to use a recursive function to remove all null elements from inner maps.
The json:
{
"topField": null,
"innerMap": {
"innerField": null
}
}
is equivalent to the following maps in java
Map map = new LinkedHashMap();
map.put("topField", null);
Map innerMap = new LinkedHashMap();
innerMap.put("innerField", null);
map.put("innerMap", innerMap);
If you apply the code to remove null values to map:
map.values().removeIf(Objects::isNull);
results in a map that is equivalent to the following manually built map:
Map map = new LinkedHashMap();
// map.put("topField", null);
Map innerMap = new LinkedHashMap();
innerMap.put("innerField", null);
map.put("innerMap", innerMap);
because it removes null values from the map, not from innerMap.
You can remove all null elements at any level as follow:
public void removeNull(Map map) {
map.values().removeIf(Objects::isNull);
for (Object value: map.values()) {
if (value instanceof Map) {
// Apply a recursion on inner maps
removeNull((Map) value);
}
}
}
And you can remove all null items as follow:
Map map = ...
removeNull(map);
As mentioned by others, you would need to do it in a recursive manner. For example, if you have function like removeNulls:
private boolean removeNulls(final Object o) {
if (Objects.isNull(o)) {
return true;
} else if (o instanceof Map) {
((Map) o).values().removeIf(MyClass::removeNulls);
}
return false;
}
Then, you could do it like:
dataDict.values().removeIf(MyClass::removeNulls);
This is just to serve as an example.

How to loop over an ArrayList which contains a map?

Currently I'm using rest-assured to hit an endpoint and in turn store any JSON data matches via an ArrayList and HashMap.
I can see that I'm receiving a response back, but how do I loop over an ArrayList when it contains an internal HashMap?
As you can see from the JSON data below, I'm expecting to output all values / matches stored within the ArrayList.
My Code:
public void apiTest() {
String position = "Attacker";
String role = "PLAYER";
Response response = given()
.spec(footballCompetitions_requestSpecification)
.when().get(EndPoint.TEAMS + EndPoint.SQUAD);
ArrayList<Map<String, ?>> allPlayers = response.path
("squad.findAll { it.role == '%s' }.findAll { it.position == '%s' }", position, role);
Example JSON Data:
{
"id": 66,
"area": {
"id": 2072,
"name": "England"
},
"activeCompetitions": [
{
"id": 2021,
"area": {
"id": 2072,
"name": "England"
},
"name": "Premier League",
"code": "PL",
"plan": "TIER_ONE",
"lastUpdated": "2019-01-03T23:39:45Z"
},
{
"id": 2001,
"area": {
"id": 2077,
"name": "Europe"
},
"name": "UEFA Champions League",
"code": "CL",
"plan": "TIER_ONE",
"lastUpdated": "2018-12-13T18:55:02Z"
}
],
"name": "Manchester United FC",
"shortName": "Man United",
"tla": "MNU",
"crestUrl": "http://upload.wikimedia.org/wikipedia/de/d/da/Manchester_United_FC.svg",
"address": "Sir Matt Busby Way Manchester M16 0RA",
"phone": "+44 (0161) 8688000",
"website": "http://www.manutd.com",
"email": "enquiries#manutd.co.uk",
"founded": 1878,
"clubColors": "Red / White",
"venue": "Old Trafford",
"squad": [
{
"id": 3188,
"name": "David De Gea",
"position": "Goalkeeper",
"dateOfBirth": "1990-11-07T00:00:00Z",
"countryOfBirth": "Spain",
"nationality": "Spain",
"shirtNumber": 1,
"role": "PLAYER"
},
{
"id": 3331,
"name": "Marcus Rashford",
"position": "Attacker",
"dateOfBirth": "1997-10-31T00:00:00Z",
"countryOfBirth": "England",
"nationality": "England",
"shirtNumber": 10,
"role": "PLAYER"
},
{
"id": 3372,
"name": "Anthony Martial",
"position": "Attacker",
"dateOfBirth": "1995-12-05T00:00:00Z",
"countryOfBirth": "France",
"nationality": "France",
"shirtNumber": 11,
"role": "PLAYER"
},
{
"id": 3662,
"name": "Romelu Lukaku",
"position": "Attacker",
"dateOfBirth": "1993-05-13T00:00:00Z",
"countryOfBirth": "Belgium",
"nationality": "Belgium",
"shirtNumber": 9,
"role": "PLAYER"
},
If you are using Java 8 or higher, then just iterate over the List first and over the Map in a nested iteration using lambda expressions:
public static void main(String[] args) {
List<Map<String, ?>> jsonList = new ArrayList<>();
Map<String, String> mapOne = new HashMap<String, String>();
mapOne.put("id", String.valueOf(66));
mapOne.put("area", "Some Area");
jsonList.add(mapOne);
jsonList.forEach(map -> {
map.forEach((key, value) -> {
System.out.println(key + ": " + value);
});
});
}
The thing you have to take care of is the String representation of value, since it is a Map<String, ?> in your code and a Map<String, String> in my example, what may lead to problems if you directly use the above example's System.out.println(key + ": " + value);.
First iterate over the list, then iterate over the Map:
for (Map<String, ?> map : allPlayers) {
for (String key : map.keySet()) {
Object player = map.get(key);
// ...
}
}

Nested JSON parsing using Java

{
"transaction": {
"id": 1,
"empid": "12345",
"details1": {
"name": "xyz",
"age": "30",
"sex": "M",
"Address": {
"Office": "office",
"Home": "Home"
}
},
"abcDetails": "asdf",
"mobile": 123455
},
"details2": {
"id": 2,
"empid": "64848",
"details": {
"name": "eryje",
"age": 3027,
"sex": "M",
"Address": {
"Office": "office",
"Home": "Home"
}
},
"abcDetails": "fhkdl",
"mobile": 389928
}
}
I am getting the data in above format. Here I did split and Iterating the data using loop. First time am getting below formatted data. So in this I want get name and age value and details1.Address.Office value also(keys are not static).
"details1": {
"name": "xyz",
"age": "30",
"sex": "M",
"Address": {
"Office": "office",
"Home": "Home"
}
}
Try using JSONObject keys() to get the key and then iterate each key to get to the dynamic value.
// searchResult refers to the current element in the array "search_result"
JSONObject questionMark = searchResult.getJSONObject("question_mark");
Iterator keys = questionMark.keys();
while(keys.hasNext()) {
// loop to get the dynamic key
String currentDynamicKey = (String)keys.next();
// get the value of the dynamic key
JSONObject currentDynamicValue = questionMark.getJSONObject(currentDynamicKey);
// do something here with the value...
}
Reference : How to parse a dynamic JSON key in a Nested JSON result?

How to use Jackson to deserialize an array of objects in side of an object?

How can I deserialize only the items array?
{
"code": 200,
"bookmarks": {
"count": 2,
"items": [
{
"status": "I",
"id": "1",
"percent": 9,
"timestamp": 1462826317475
},
{
"status": "I",
"id": "2",
"percent": 10,
"timestamp": 1462909994981
}
],
"links": []
}
}
I know for an array I can do something like this:
List<MyClass> myObjects = mapper.readValue(jsonInput, new TypeReference<List<MyClass>>(){});
Can I specify only deserializing items?
Use ObjectReaders at(JsonPointer) method to specify sub-tree to bind. Something like:
Item[] items = mapper.readerFor(Item[].class).at("/bookmarks/items").readValue();

Categories