Group two object attributes in a list - java

I would like to transform a duplicate object with different values to a list in java, as follows
[
{
"code": "code",
"active": true,
"car": "Sedan"
},
{
"code": "code",
"active": true,
"car": "R4"
},
{
"code": "code2",
"active": false,
"car": "Sedan"
},
{
"code": "code2",
"active": false,
"car": "R4"
}
]
ClassOne
public class Car{
private String code;
private boolean active;
private String car;
}
if "code" and "active" are the same, I would like to group them in a single object
[
{
"code": "code",
"active": true,
"name": {
"cars": [
{
"brand": "Sedan"
},
{
"brand": "R4"
}
]
}
},
{
"code": "code2",
"active": false,
"name": {
"cars": [
{
"brand": "Sedan"
},
{
"brand": "R4"
}
]
}
}
]
Class parse
public class CarParse{
private String code;
private String active;
private Name name;
}
public class Name{
private List<Brand> cars;
}
public class Brand{
private String brand;
}
Then it would be to go from ClassOne to ClassParse,transforming the objects grouped by "code" and "active

I find writing the non java-stream easier first.
For example, group the input objects on code-active will form a unique pair that you can loop over, then build the final list from
public List<CarParse> getParsed(List<Car> cars) {
Map<String, CarParse> codeMap = new HashMap<>();
for (Car c : cars) {
CarParse cp;
List<Brand> brands;
String code = c.getCode();
boolean active = c.isActive();
String group = String.format("%s-%s", code, active);
Brand b = new Brand(c.getCar());
if (codeMap.containsKey(group)) {
cp = codeMap.get(group);
brands = cp.getName().getCars();
brands.add(b);
} else {
brands = new ArrayList<>();
brands.add(b);
Name n = new Name(brands);
cp = new CarParse(code, active, n);
codeMap.put(group, cp);
}
}
return new ArrayList<>(codeMap.values());
}
Stream version
return new ArrayList<>(cars.stream().collect(Collectors.toMap(
c -> String.format("%s-%s", c.getCode(), c.isActive()),
c -> {
ArrayList<Brand> brands = new ArrayList<>();
brands.add(new Brand(c.getCar()));
return new CarParse(c.getCode(), c.isActive(), new Name(brands));
},
(v1, v2) -> {
if (v1.isActive() == v2.isActive() && (v1.getCode().equals(v2.getCode()))) {
for (Brand b : v2.getName().getCars()) {
v1.getName().getCars().add(b);
}
return v1;
}
return v1;
})).values());
Output
[ {
"code" : "code",
"active" : true,
"name" : {
"cars" : [ {
"brand" : "Sedan"
}, {
"brand" : "R4"
} ]
}
}, {
"code" : "code2",
"active" : false,
"name" : {
"cars" : [ {
"brand" : "Sedan"
}, {
"brand" : "R4"
} ]
}
} ]

Related

How to Convert Tree Structure List to Json

I have query result like this :
DEPT_NAME
DEPT_R_IDX
DEPT_IDX
DEPT_LEVEL
root
0
1
0
dept_0
1
2
1
dept_1
1
3
1
dept_1_0
3
4
2
dept_1_1
3
5
2
dept_2
1
6
1
dept_2_0
6
7
2
dept_2_0_1
7
8
3
DEPT_IDX is PRIMARY KEY, DEPT_LEVEL is TREE DEPTH, DEPT_R_IDX is parent's DEPT_IDX
I stored this data in Java's List.
List<HashMap<String, String>> DEPT_LIST;
I want convert this List to Json like this :
[
{
"title": "root",
"key": "1",
"expanded": true,
"folder": true,
"children": [
{
"key": "2",
"title": "dept_0",
"expanded": true,
"folder": true
},
{
"key": "3",
"title": "dept_1",
"expanded": true,
"folder": true,
"children": [
{
"key": "4",
"title": "dept_1_0",
"expanded": true,
"folder": true
},
{
"key": "5",
"title": "dept_1_1",
"expanded": true,
"folder": true
}
]
},
{
"key": "6",
"title": "dept_2",
"expanded": true,
"folder": true,
"children": [
{
"key": "7",
"title": "dept_2_0",
"expanded": true,
"folder": true,
"children": [
{
"key": "8",
"title": "dept_2_1",
"expanded": true,
"folder": true
}
]
}
]
}
]
}
]
result tree using this json data(using fancytree)
i tried this in browser side, but it's too low performance to make json structure
Instead of storing your data in a List<HashMap<String, String>>, store them in a List<Node> where class Node is something like this:
public class Node {
private String title;
private String key;
private boolean expanded;
private boolean folder;
private List<Node> children;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public boolean isExpanded() {
return expanded;
}
public void setExpanded(boolean expanded) {
this.expanded = expanded;
}
public boolean isFolder() {
return folder;
}
public void setFolder(boolean folder) {
this.folder = folder;
}
public List<Node> getChildren() {
return children;
}
public void setChildren(List<Node> children) {
this.children = children;
}
}
Then use Jackson or Gson to generate the corresponding Json.

Java Stream to aggregate list of JSONs into groups

I have list of objects in java, like this.
[
{
"applicationNumber": "100400",
"users": "A",
"category": "student"
},
{
"applicationNumber": "100400",
"users":"B",
"category": "student"
},
{
"applicationNumber": "100400",
"users":"C",
"category": "neighbour"
},
{
"applicationNumber": "100400",
"users": "D",
"category": "neighbour"
},
{
"applicationNumber": "200543",
"users": "C",
"category": "student"
},
{
"applicationNumber": "200543",
"users": "A",
"category": "student"
},
{
"applicationNumber": "200543",
"users":"D",
"category": "friend"
}
]
I want to group users as list (order does not matter) for each category for every applicationNumber. Can refer below json to get the idea.
[
{
"applicationNumber": "100400",
"users": [
"A",
"B"
],
"category": "student"
},
{
"applicationNumber": "100400",
"users": [
"C",
"D"
],
"category": "neighbour"
},
{
"applicationNumber": "200543",
"users": [
"C",
"A"
],
"category": "student"
},
{
"applicationNumber": "200543",
"users": [
"D"
],
"category": "friend"
}
]
I am able to this using a for loop, HashMap and if else conditions. I want to use Java 8 stream to achieve the same . Can anyone help me , I am new to java.
PS: Thank you in advance
I think using streams here is a little bit overengineering but you can to that in two steps. First you need to use Collectors.groupingBy() to group yours pojos into map of lists. Next you need to reduce each list to a single value by using stream().reduce().
ObjectMapper mapper = new ObjectMapper()
.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
List<Application> applications = Arrays.asList(mapper.readValue(json, Application[].class));
List<Application> groupedApplications = applications.stream()
.collect(Collectors.groupingBy(ApplicationKey::of, Collectors.toList()))
.values().stream()
.map(apps -> apps.stream().reduce(Application::merge))
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
Application.java:
public class Application {
private String applicationNumber;
private String category;
private List<String> users = new ArrayList<>();
public static Application merge(Application first, Application second) {
assert ApplicationKey.of(first).equals(ApplicationKey.of(second));
Application merged = new Application(first.applicationNumber, first.category, first.getUsers());
merged.users.addAll(second.getUsers());
return merged;
}
//constructor, getters, setters
}
ApplicationKey.java
public class ApplicationKey {
private String applicationNumber;
private String category;
public static ApplicationKey of(Application application) {
return new ApplicationKey(application.getApplicationNumber(), application.getCategory());
}
public ApplicationKey(String applicationNumber, String category) {
this.applicationNumber = applicationNumber;
this.category = category;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ApplicationKey that = (ApplicationKey) o;
return Objects.equals(applicationNumber, that.applicationNumber) &&
Objects.equals(category, that.category);
}
#Override
public int hashCode() {
return Objects.hash(applicationNumber, category);
}
//getters, setters
}

How to iterate a list recursively on condition comparison using java8 stream

I have two sets of json , which consist of recursive children.
I want to find last child in that list which will contain "code", I want to find all "code" values from that jsons.
Sample1:
{
"totalSize": 1,
"data": [
{
"level": "sites",
"children": [
{
"level": "sites",
"children": [
{
"level": "segments",
"children": [
{
"level": "assets",
"code": "1"
},
{
"level": "assets",
"code": "2"
},
{
"level": "assets",
"code": "3"
},
{
"level": "assets",
"code": "4"
},
{
"level": "assets",
"code": "5"
},
{
"level": "assets",
"code": "6"
}
]
}
]
}
]
}
]
}
Sample2:
{
"totalSize": 1,
"data": [
{
"level": "sites",
"children": [
{
"level": "segments",
"children": [
{
"level": "assets",
"code": "1"
},
{
"level": "assets",
"code": "2"
},
{
"level": "assets",
"code": "3"
},
{
"level": "assets",
"code": "4"
},
{
"level": "assets",
"code": "5"
},
{
"level": "assets",
"code": "6"
}
]
}
]
}
]
}
The entities are as follows:
public class HierarchyResponse {
private Integer totalSize;
private List<Data> data;
}
public class Data {
private List<Children> children;
private String level;
}
public class Children {
private String level;
private List<Children> children;
}
I have tried but didnot succeed:
List<Children> children = response
.getData()
.get(0)
.getChildren()
.stream().filter(t -> t.getLevel().equalsIgnoreCase("assets"))
.collect(Collectors.toList());
you can create a method which recursively flatmaps the children
private static Stream<Children> toStream(Children children) {
if (children != null) {
return Stream.concat(Stream.of(children), children.getChildren().stream().flatMap(c -> toStream(c)));
} else {
return Stream.empty();
}
}
and use it as
List<Children> children = response.getData().stream()
.flatMap(d -> d.getChildren().stream())
.flatMap(c -> toStream(c))
.filter(t -> t.getLevel().equalsIgnoreCase("assets"))
.collect(Collectors.toList());
for example
Children b1as2 = new Children("assets", "2");
Children b1as1 = new Children("assets", "1");
Children b1seg1 = new Children("segments", "0", List.of(b1as1, b1as2));
Children b1s1 = new Children("sites", "0", List.of(b1seg1));
Children b1 = new Children("sites", "0", List.of(b1s1));
Data data = new Data(List.of(b1));
HierarchyResponse response = new HierarchyResponse(List.of(data));
will print [Children{level='assets', code='1'}, Children{level='assets', code='2'}]
Another solution is not using Stream API
private static List<Children> findAssetsChildren(List<Children> children) {
List<Children> result = new LinkedList<>();
for(Children ch: children) {
if(ch.getLevel().equals("assets")) {
result.add(ch);
}
result.addAll(findAssetsChildren(ch.getChildren()));
}
return result;
}
//...
List<Children> children = findAssetsChildren(response.getData()
.get(0).getChildren());

JSON is duplicated when writing from object to file

I am using Jackson to convert JSON into an object and vice versa. However, when writing back the object back as JSON it is duplicated, like so:
{
"Users": [
{
"name": "Steve",
"buckets": [
{
"bucketName": "stevesbucket",
"permissions": [
"CREATE",
"READ",
"UPDATE",
"DELETE"
],
"owner": "Steve"
},
{
"bucketName": "NEW BUCKET 2",
"permissions": [
"CREATE",
"READ",
"UPDATE",
"DELETE"
],
"owner": "Steve"
}
]
},
{
"name": "Jeff",
"buckets": [
{
"bucketName": "jeffsbucket",
"permissions": [
"CREATE",
"READ",
"UPDATE",
"DELETE"
],
"owner": "Jeff"
},
{
"bucketName": "stevesbucket",
"permissions": [
"READ"
],
"owner": "Steve"
}
]
}
],
"users": [
{
"name": "Steve",
"buckets": [
{
"bucketName": "stevesbucket",
"permissions": [
"CREATE",
"READ",
"UPDATE",
"DELETE"
],
"owner": "Steve"
},
{
"bucketName": "NEW BUCKET 2",
"permissions": [
"CREATE",
"READ",
"UPDATE",
"DELETE"
],
"owner": "Steve"
}
]
},
{
"name": "Jeff",
"buckets": [
{
"bucketName": "jeffsbucket",
"permissions": [
"CREATE",
"READ",
"UPDATE",
"DELETE"
],
"owner": "Jeff"
},
{
"bucketName": "stevesbucket",
"permissions": [
"READ"
],
"owner": "Steve"
}
]
}
]
}
Where there should only be one "Users" field. I have tried playing with the visibility settings of my object mapper with this:
ObjectMapper mapper = new ObjectMapper();
mapper.getSerializationConfig().getDefaultVisibilityChecker()
.withFieldVisibility(JsonAutoDetect.Visibility.ANY)
.withGetterVisibility(JsonAutoDetect.Visibility.NONE)
.withSetterVisibility(JsonAutoDetect.Visibility.NONE)
.withCreatorVisibility(JsonAutoDetect.Visibility.NONE);
However this hasn't made a difference. I think something in my users.java file may be causing an issue, as I have extra methods such as addBucket:
public static class Bucket
{
private String bucketName;
private String[] permissions;
private String owner;
public void setBucket(String bucketName, String[] permissions, String owner)
{
this.bucketName = bucketName;
this.permissions = permissions;
this.owner = owner;
}
public String getBucketName()
{
return bucketName;
}
public String[] getPermissions()
{
return permissions;
}
public String getOwner()
{
return owner;
}
}
public static class User
{
private String name;
private Bucket[] buckets;
public String getName()
{
return name;
}
public Bucket[] getBuckets()
{
return buckets;
}
#JsonIgnore
public void addBucket(String bucketName, String[] permissions, String owner)
{
Bucket[] temp = new Bucket[buckets.length+1];
for(int i = 0; i < buckets.length; ++i)
{
temp[i] = buckets[i];
}
temp[temp.length-1] = new Bucket();
temp[temp.length-1].setBucket(bucketName, permissions, owner);
buckets = temp;
}
}
public User[] Users;
public User[] getUsers()
{
return Users;
}
public void setUsers(User[] newUsers)
{
Users = newUsers;
}
Are there some properties I need to add to some things in users.java? Or are there other visibility settings I should be using with my ObjectMapper?
public User[] Users;
public User[] getUsers()
{
return Users;
}
Jackson is serializing the public member Users as "Users" and the getUsers function as "users". Users being private should fix this, and it is good practice for it to be so

Jackson: Convert JSON to object: ArrayList of objects with arraylist of objects with arraylist of objects

I have an android app which makes a call do rest api.
It returns a list of restaurants. Each restaurant has list of categories. Each category can have multiple dishes.
When I deserialize this list, I have nice ArrayList of Restaurants, each Restaurant also has an ArrayList of Categories, but in Category class, ArrayList of Dishes is null.
Does anyone has any idea how to solve this?
JSON response: (it's actually a object of type ServiceResponse)
{
"requestToken" : 0,
"responseCode" : 2,
"objectType" : "com.test.Restaurant",
"error" : null,
"data" : [ {
"id" : 1,
"name" : "Name",
"addressCity" : "Warszawa",
"addressStreet" : "Kopernika",
"addressStreetNumber" : "4",
"addressApartmentNumber" : "17",
"categories" : [ {
"id" : 1,
"restaurantId" : 1,
"name" : "Breakfast",
"description" : "Description",
"dishes" : [ {
"id" : 11,
"categoryId" : 1,
"name" : "Dish 1",
"description" : "Descr",
"price" : 8.99,
"photoId" : 51
}, {
"id" : 21,
"categoryId" : 1,
"name" : "Dish 2",
"description" : "Descr",
"price" : 6.99,
"photoId" : 41
}, {
"id" : 31,
"categoryId" : 1,
"name" : "Dish 3",
"description" : "Descr",
"price" : 14.99,
"photoId" : 61
} ]
}, {
"id" : 2,
"restaurantId" : 1,
"name" : "Obiady",
"description" : "Opis kategorii",
"dishes" : [ {
"id" : 41,
"categoryId" : 2,
"name" : "Danie 4",
"description" : "Descr",
"price" : 6.99,
"photoId" : 0
}, {
"id" : 61,
"categoryId" : 2,
"name" : "Danie 5",
"description" : "Descr",
"price" : 8.0,
"photoId" : 0
}, {
"id" : 101,
"categoryId" : 2,
"name" : "Nowy obiad",
"description" : "",
"price" : 111.0,
"photoId" : 0
} ]
}, {
"id" : 3,
"restaurantId" : 1,
"name" : "Pasta",
"description" : "Opis kategorii",
"dishes" : [ ]
}, {
"id" : 4,
"restaurantId" : 1,
"name" : "Przystawki",
"description" : "Opis kategorii",
"dishes" : [ ]
}, {
"id" : 11,
"restaurantId" : 1,
"name" : "Some category",
"description" : "test",
"dishes" : [ ]
}, {
"id" : 51,
"restaurantId" : 1,
"name" : "Sushi",
"description" : "",
"dishes" : [ ]
} ]
}]
}
Parsing result with Jackson:
ServiceResponse response = getServiceOutput(params, httpMethod);
if(response.getData() != null && response.getData().size() > 0) {
try {
// convert data array from object to class defined in objectType (in JSON)
ObjectMapper mapper = new ObjectMapper();
Class clazz = Class.forName( response.getObjectType() );
ArrayList list = (mapper.convertValue(response.getData(),
TypeFactory.defaultInstance().constructCollectionType(
ArrayList.class, clazz)));
broadcastIntent.putExtra("result", list);
broadcastIntent.putExtra("response_code", ServiceResponse.OK);
} catch (Exception e) {
broadcastIntent.putExtra("response_code", ServiceResponse.ERROR);
e.printStackTrace();
}
}
This works for me. The reason I'm declaring the classes (Response, Restaurant etc) as static is because Jackson cannot deserialize non-static inner classes.
public class Test {
static String jsonString = "{ \"requestToken\":0, \"responseCode\":2, \"objectType\":\"com.test.Restaurant\", \"error\":null, \"data\":[ { \"id\":1, \"name\":\"Name\", \"addressCity\":\"Warszawa\", \"addressStreet\":\"Kopernika\", \"addressStreetNumber\":\"4\", \"addressApartmentNumber\":\"17\", \"categories\":[ { \"id\":1, \"restaurantId\":1, \"name\":\"Breakfast\", \"description\":\"Description\", \"dishes\":[ { \"id\":11, \"categoryId\":1, \"name\":\"Dish 1\", \"description\":\"Descr\", \"price\":8.99, \"photoId\":51 }, { \"id\":21, \"categoryId\":1, \"name\":\"Dish 2\", \"description\":\"Descr\", \"price\":6.99, \"photoId\":41 }, { \"id\":31, \"categoryId\":1, \"name\":\"Dish 3\", \"description\":\"Descr\", \"price\":14.99, \"photoId\":61 } ] }, { \"id\":2, \"restaurantId\":1, \"name\":\"Obiady\", \"description\":\"Opis kategorii\", \"dishes\":[ { \"id\":41, \"categoryId\":2, \"name\":\"Danie 4\", \"description\":\"Descr\", \"price\":6.99, \"photoId\":0 }, { \"id\":61, \"categoryId\":2, \"name\":\"Danie 5\", \"description\":\"Descr\", \"price\":8.0, \"photoId\":0 }, { \"id\":101, \"categoryId\":2, \"name\":\"Nowy obiad\", \"description\":\"\", \"price\":111.0, \"photoId\":0 } ] }, { \"id\":3, \"restaurantId\":1, \"name\":\"Pasta\", \"description\":\"Opis kategorii\", \"dishes\":[ ] }, { \"id\":4, \"restaurantId\":1, \"name\":\"Przystawki\", \"description\":\"Opis kategorii\", \"dishes\":[ ] }, { \"id\":11, \"restaurantId\":1, \"name\":\"Some category\", \"description\":\"test\", \"dishes\":[ ] }, { \"id\":51, \"restaurantId\":1, \"name\":\"Sushi\", \"description\":\"\", \"dishes\":[ ] } ] } ] }";
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Response res = mapper.readValue(jsonString, Response.class);
}
public static class Response {
public String requestToken;
public List<Restaurant> data;
public Response(){}
public String getRequestToken() {
return requestToken;
}
public void setRequestToken(String requestToken) {
this.requestToken = requestToken;
}
public List<Restaurant> getData() {
return data;
}
public void setData(List<Restaurant> data) {
this.data = data;
}
}
public static class Restaurant {
public List<Category> categories;
public List<Category> getCat() {
return categories;
}
public void setCat(List<Category> categories) {
this.categories = categories;
}
public Restaurant(){}
}
public static class Category {
public List<Dish> dishes;
public Category(){}
public List<Dish> getDishes() {
return dishes;
}
public void setDishes(List<Dish> dishes) {
this.dishes = dishes;
}
}
public static class Dish {
public String id, categoryId, name, description;
public float price;
public int photoId;
public Dish(){}
}
}

Categories