OneToMany Mapping POJO to JSON Spring - java

I use PostgreSQL and I have these tables, address and company with relation OneToMany on address to company. I want to retrieve a list of addresses which each of them contains a list of companies. I implemented the join between these two tables and I retrieved the data below:
When execute this query:
select ad.id,ad.geolocation, ca.id, ca.name_en, ca.name_el from
address as ad right join company_address as ca on (ad.id = ca.address_id);
I retrieved this table and I want to map it in JSON format as below:
1;"0101000020E6100000C006335CD3043840504BBDB89EC14140"; 6; "Petros";
1;"0101000020E6100000C006335CD3043840504BBDB89EC14140"; 12; "Shop3";
1;"0101000020E6100000C006335CD3043840504BBDB89EC14140"; 13; "Shop3";
7;"0101000020E6100000A03418D984063840D8DD978E94C14140"; 7; "Mike";
7;"0101000020E6100000A03418D984063840D8DD978E94C14140"; 14; "Shop5";
7;"0101000020E6100000A03418D984063840D8DD978E94C14140"; 15; "Shiop8";
9;"0101000020E6100000804127840E163840CC28965B5AC64140"; 10; "Shop2";
9;"0101000020E6100000804127840E163840CC28965B5AC64140"; 11; "Shop3";
Do you know how can convert the results in the format below using an elegant way?
POJO:
private Long id;
private Geolocation geolocation;
private List<ShopObj> shops;
ShopObj Class:
class ShopObj{
private Long id;
private String nameEn;
}
Json Data:
{
"code": "200",
"data": [
{
"id": 1,
"geolocation": "0101000020E6100000C006335CD3043840504BBDB89EC14140",
"shops": [
{
"id": 6,
"nameEn": "Petros"
},
{
"id": 12,
"nameEn": "Shop3"
},
{
"id": 13,
"nameEn": "Shop3"
}
]
},
{
"id": 7,
"geolocation": "0101000020E6100000A03418D984063840D8DD978E94C14140",
"shops": [
{
"id": 7,
"nameEn": "Mike"
},
{
"id": 14,
"nameEn": "Shop5"
},
{
"id": 15,
"nameEn": "Shiop8"
}
]
},
{
"id": 9,
"geolocation": "0101000020E6100000804127840E163840CC28965B5AC64140",
"shops": [
{
"id": 10,
"nameEn": "Shop2"
},
{
"id": 11,
"nameEn": "Shop3"
}
]
}
]
}

Sure.
Create an empty Map<Long, POJO>, containing the POJOs as values, and their ID as keys
Iterate throug your rows.
For each row, get the POJO ID, and get the corresponding POJO from the map. If it's not in the map yet, create the POJO and add it to the map
create a Shop for the current row, and add the shop to the list of shops of the POJOs obtained/created at the previous step
At the end, the values() of the map is the Collection<POJO> that you want to serialize to JSON.

I implemented the code below and worked for my case, but do you know if is there and auto way to map my data?
List<ProductTest> productTests = productRepository.getProductTest();
Map<Long, ProductT> products = new HashMap<Long, ProductT>();
for (ProductTest pt : productTests) {
ProductT productT = products.get(pt.getId());
if (productT == null) {
productT = new ProductT(pt.getId(), pt.getNameEl(), new ArrayList<MediaT>());
products.put(pt.getId(), productT);
}
MediaT mediaT = new MediaT(pt.getMediaId(), pt.getMultimediaPath());
productT.getMediaList().add(mediaT);
}
return products.values();

Related

How to convert flat list to nested tree in java with a nested set model?

I'm using the nested set model to store category hierarchies in my postgres database. Im able to query the tree for a given category ID and see all of its children and the depths they sit at. The response for that query looks like this:
[
{
"id": "07e0b6c2-49cd-440b-a900-0f3d7ab88022",
"categoryId": "80d15a99-9e42-4b72-b44b-0b222ca5173e",
"name": "Root",
"lft": 1,
"rgt": 18,
"depth": 0
},
{
"id": "ae53be00-c312-4cd5-a6b2-6baeaf760577",
"categoryId": "9b8bca09-2447-494c-be0d-0b3af7d30671",
"name": "Cat A",
"lft": 2,
"rgt": 9,
"depth": 1
},
{
"id": "0a5d4b90-29b9-4c50-a436-d129dc6983ea",
"categoryId": "d06a143b-523e-4136-8a17-1049abbf76f4",
"name": "Cat B",
"lft": 3,
"rgt": 4,
"depth": 2
},
{
"id": "11421455-abc0-464c-8bd0-e2d79302c270",
"categoryId": "5af63d5b-f480-4620-8393-f4b93f7972e0",
"name": "Cat D",
"lft": 5,
"rgt": 6,
"depth": 2
},
{
"id": "4463dbce-a2bf-42fe-a864-59309ba54d22",
"categoryId": "21191930-a5b9-4868-883f-3798f29d70a3",
"name": "Cat E",
"lft": 7,
"rgt": 8,
"depth": 2
},
{
"id": "0f40e7a0-e6eb-44a4-a9bd-b61512daa236",
"categoryId": "34b127e8-7a8f-40b3-9b7e-63c8d507cc7b",
"name": "Cat F",
"lft": 10,
"rgt": 11,
"depth": 1
},
{
"id": "87e9991e-085c-47a5-8357-79c0e467b8ec",
"categoryId": "dfbbaac7-dda3-4f34-a787-183803f8e6fa",
"name": "Cat G",
"lft": 12,
"rgt": 17,
"depth": 1
},
{
"id": "8a95b0ab-cf74-4083-9d17-40e70468350a",
"categoryId": "f7f04485-d089-4a5d-98cd-20b0abeba8fc",
"name": "Cat H",
"lft": 13,
"rgt": 14,
"depth": 2
},
{
"id": "dccee476-af73-4eb6-a595-f862984d4af6",
"categoryId": "0eb165ec-0347-4336-8fc2-35c124bf26f2",
"name": "Cat I",
"lft": 15,
"rgt": 16,
"depth": 2
}
]
You'll notice the tree is already flattened. I'm trying to put the above structure into a nested tree structure that I can return as JSON to my UI. Ideally something that looks like this (omitted data for brevity):
[
{
"name": "Cat A",
"children": [
{
"name": "Cat B",
"children": [
{
"name": "Cat C"
},
{
"name": "Cat D"
}
]
},
{
"name": "Cat E",
"children": [
{
"name": "Cat F"
}
]
}
]
}
]
I've tried a bunch of options with the most recent being streaming the original list into a map and then collecting its values based on the depth each node sits at. This doesn't get me all the way what I'm looking for as it groups nodes that are at the same level but not necessarily part of the same tree.
List<List<CategoryTree>> listOfLists = new ArrayList<>(
categoryTreeList.stream()
.collect(Collectors.groupingBy(CategoryTree::getDepth))
.values());
Seems like I should be trying to key off of the lft and rgt values as well to determine if a node in a given list has any children which would be indicated by a spread greater than a value of 1.
How can I solve this?
I would suggest that you create a class that holds your structure data rather than trying to use a raw map.
class Node {
private final String id;
private final String category;
private Node left = null;
private Node right = null;
public Node(String id, String category) {
this.id = id;
this.category = category;
}
// left and right setters
}
You will need to build the structure in 2 passes.
The first will be creating the nodes and adding them to a list so that their indices can be used.
List<Node> nodes = new ArrayList<>();
for (row: queryResult) {
nodes.add(new Node(row.id(), row.category()));
}
In the second pass you build the structure:
int i = 0;
for (row: queryResult) {
Node node = nodes.get(i++);
if (row.lft())
node.setLeft(nodes.get(row.lft()));
if (row.rgt())
node.setRight(nodes.get(row.rgt()));
}
There are ways of doing it in 1 pass and then resolving forward references but I really doubt it'd be worth it.
If your root node is not guaranteed to be the first in the list then you would need to look through the list finding a node that is not a child of any other node.
There's really no need to store or use the depth unless you particularly need it for something else. And even then it's best to derive it (by maintaining a parent field and walking up the hierarchy) unless the structure is guaranteed to be static.
Converting to json is best done with a library such a gson and then a custom converter for your class that uses recursion to convert the whole structure.

Group List of Objects to Create an Extended Object List

I have these sample records from database which is a result of group by query:
[
{
"name": "Troy",
"acct": 1123,
"type": " Savings",
"total": 50
},
{
"name": "Larry",
"acct": 4233,
"type": " Savings",
"total": 200
},
{
"name": "Troy",
"acct": 1123,
"type": " Current",
"total": 120
},
{
"name": "Larry",
"acct": 4233,
"type": " Current",
"total": 220
}
]
Now, i need to create a report that looks like so:
[
{
"name": "Troy",
"acct": 1123,
"totalSavings": 50,
"totalCurrent": 120
},
{
"name": "Larry",
"acct": 4233,
"totalSavings": 200,
"totalCurrent": 220
}
]
.
public class DbTrans {
private String name;
private String acct;
private String type;
private double total;
// getters and setters
...
}
I have tried using some lambda techniques like the one below, but i'm still not getting close to the solution i desire.
Map<String, List<DbTrans>> collect = transList.stream().collect(
Collectors.groupingBy(f -> f.getType()));
First of all the response Dto is not the same as the request Dto, what I suggest is to create a new Response class lests call it:
public class DbTransResponse {
private String name;
private String acct;
private double totalSavings;
private double totalCurrent;
// getters and setters
}
then the result can be like so :
List<DbTransResponse> result = transList.stream()
.collect(Collectors.groupingBy(DbTrans::getAcct))
.values().stream()
.map(trans -> new DbTransResponse(
trans.get(0).getName(),
trans.get(0).getAcct(),
trans.get(0).getTotal(),
trans.get(1).getTotal()
)).collect(Collectors.toList());
I consider that the list should contain two entries of each name so the you can get totalSavings from the first entry trans.get(0).getTotal() and totalCurrent from the second trans.get(1).getTotal().
If you are not sure you can use conditions to fill your object for example you can check if there are two elements if not set a default value.
Ideone demo
Outputs
DbTransResponse{name='Larry', acct='4233', totalSavings=200.0, totalCurrent=220.0}
DbTransResponse{name='Troy', acct='1123', totalSavings=50.0, totalCurrent=120.0}
you can use Collectors::toMap for this purpose (with a single collect operation)
Map<Integer, DbTransResponse> collect = transList.stream()
.collect(Collectors.toMap(DbTrans::getAcct,
DbTransResponse::new,
DbTransResponse::merge));
Collection<DbTransResponse> result = collect.values();
here is merge method in DbTransResponse class
static DbTransResponse merge(DbTransResponse r1, DbTransResponse r2) {
return new DbTransResponse(
r1.name, r1.acct,
r1.totalSavings + r2.totalSavings,
r1.totalCurrent + r2.totalCurrent
);
}
and an additional constructor for DbTransResponse class, though you can move this logic to a method
DbTransResponse(DbTrans dbTrans) {
this.name = dbTrans.getName();
this.acct = dbTrans.getAcct();
this.totalSavings = "Savings".equals(dbTrans.getType()) ? dbTrans.getTotal() : 0;
this.totalCurrent = "Current".equals(dbTrans.getType()) ? dbTrans.getTotal() : 0;
}
demo

Create POJO from json generated by key=>value Array

I want create a pojo from a json like this
{
"1": [
{
"idmapel": 1,
"label": "Fisika"
},
{
"idmapel": 2,
"label": "Kimia"
},
{
"idmapel": 3,
"label": "Biologi"
},
{
"idmapel": 4,
"label": "Matematika"
},
],
"2":[
{
"idmapel": 1,
"label": "Fisika"
}
]
}
when i generate from http://www.jsonschema2pojo.org/ it created a 1 and 2 class, but imagine if i have more than 2 keys. i want to be able to access element by something like this ObjectClass::getList(1) or ObjectClass::getList(2)
You can use a Map to do this.
Map<String, List<Pojo>> map = deserialize(jsonSring);
where Pojo is the class which has fields idmapel and label, deserialize is a method which deserializes the json to object and jsonString is the json string value to deserialize. Then you can access lists with keys
List<Pojo> list1 = map.get("1");
List<Pojo> list2 = map.get("2");

How to get only List of IDs of JSON Objects?

I have some problem. I get a Response from my RESTful WebServices with JSON Objects in a list. For example:
I get a List of these elements and I want just to get the id from the benutzerId.
[{
"benutzerId": {
"benutzername": "Nenzema",
"geschlecht": "m",
"id": 1,
"nachname": "Marinovic",
"passwort": "test1",
"vorname": "Nenad"
},
"hochzeitId": {
"id": 4
},
"id": 1,
"isbrautbenutzer": true
}, {
"benutzerId": {
"benutzername": "dnikolic",
"geschlecht": "m",
"id": 2,
"nachname": "Nikolic",
"passwort": "test2",
"vorname": "Djordje"
},
"hochzeitId": {
"id": 4
},
"id": 4,
"isbrautbenutzer": false
}]
I already made it, to get just the benutzer, but I dont know how to return a json Object where only the id is in this object, because I dont want to send the whole data to the requester.
[{
"benutzername": "Nenzema",
"geschlecht": "m",
"id": 1,
"nachname": "Marinovic",
"passwort": "test1",
"vorname": "Nenad"
}, {
"benutzername": "dnikolic",
"geschlecht": "m",
"id": 2,
"nachname": "Nikolic",
"passwort": "test2",
"vorname": "Djordje"
}]
So my aim would be to get something like this:
[{
"id": 1
}, {
"id": 2
}]
This is the query for the first list:
#NamedQuery(name = "Hochzeitsplan.findByBenutzerId",
query = "SELECT h FROM Hochzeitsplan h WHERE h.benutzerId.id = :benutzerId"),
These two queries I also tested, but its not working..
#NamedQuery(name = "Hochzeitsplan.getHochzeitsuserByHochzeitsId",
query = "SELECT DISTINCT h.benutzerId FROM Hochzeitsplan h WHERE h.hochzeitId.id = :hochzeitId"),
#NamedQuery(name = "Hochzeitsplan.findBenutzerIdByHochzeitsId",
query = "SELECT h.benutzerId.id FROM Hochzeitsplan h WHERE h.hochzeitId.id = :hochzeitId"),
This is the method I use for getting the Response with the List:
#GET
#Path("/hochzeitIDneu={hochzeitId}")
#Produces({MediaType.APPLICATION_JSON})
public Response getListByHochzeitID(#PathParam("hochzeitId") Integer hochzeitId) {
Query query=em.createNamedQuery("Hochzeitsplan.findByHochzeitId",Hochzeitsplan.class)
.setParameter("hochzeitId", hochzeitId);
List<Hochzeitsplan> a =query.getResultList();
a.sort(new Comparator<Hochzeitsplan>() {
#Override
public int compare(Hochzeitsplan o1, Hochzeitsplan o2)
{
return o1.getId().compareTo(o2.getId());
}
});
if(a.isEmpty()) {
ErrorManager error1 = new ErrorManager();
return Response.status(400).entity(error1.getError(Enums.ErrorResponses.NORESULTS)).build();
} else {
return Response.ok(a.toArray(new Hochzeitsplan[a.size()])).build();
}
}
Use a JsonPath Evaluator like Jayway Jsonpath.
https://mvnrepository.com/artifact/com.jayway.jsonpath/json-path
Your json path can be something like this
$[*]['benutzerId'].id
This would generate output something like
[
1,
2
]
U can try this online https://jsonpath.herokuapp.com

java - Jackson Json traverse encapsulated tree

I have a schema like this (simplified):
{
"range": {
"offset": 0,
"limit": 1,
"total": 2
},
"items": [
{
"id": 11,
"name": "foo",
"children": [
{
"id": 112,
"name": "bar",
"children": [
{
"id": 113,
"name": "foobar",
"type": "file"
}
],
"type": "folder"
},
{
"id": 212,
"name": "foofoo",
"type": "file"
}
],
"type": "room"
},
{
"id": 21,
"name": "barbar",
"type": "room"
}
]
}
I need to read only specific values like "id" from the first room (item). For this I need to iterate trough all items on every level (n items for root, n items for n children) with type folder or file.
For now i have this code:
POJO
public static class Item {
public int id;
}
Jackson Tree Iteration
ObjectMapper mapper = new ObjectMapper();
com.fasterxml.jackson.databind.JsonNode root = mapper.readTree(JSON);
root = root.get("items").get(0);
TypeReference<List<Item>> typeRef = new TypeReference<List<Item>>(){};
List<Item> list = mapper.readValue(root.traverse(), typeRef);
for (Item f : list) {
System.out.println(f.id);
}
How can i get all id's of all children in all items with specific type?
How to avoid the "Unrecognized field" exception without defining the whole schema?
Thank you very much for your help!
Try using java8 functions it has lot to do it in lesser lines ,
ObjectMapper mapper = new ObjectMapper();
Pass your json value
Map obj = mapper.readValue(s, Map.class);
List<Object> items= (List<Object>) obj.get("items");
Object[] Ids= items
.stream()
.filter(items-> ((Map)items).get("type").equals("room"))
.toArray()
Use the readTree(...) method to parse the JSON without needing to define the entire schema and find Nodes called "id".
You can then use findValues("id") to get the List of values back.

Categories

Resources