Java JSON tree structure creation from existing data - java
Need your help with creating a JSON tree structure from available data. This is what is available with me right now,
The same data in json format is as below,
{
"employees": [
{
"empId": 1,
"empName": "Alex",
"empGroupId": 3,
"empLevel": 0
},
{
"empId": 42,
"empName": "Sam",
"empGroupId": 3,
"empLevel": 1
},
{
"empId": 22,
"empName": "Max",
"empGroupId": 3,
"empLevel": 2
},
{
"empId": 54,
"empName": "Ervin",
"empGroupId": 3,
"empLevel": 3
},
{
"empId": 1,
"empName": "Alex",
"empGroupId": 5,
"empLevel": 0
},
{
"empId": 42,
"empName": "Sam",
"empGroupId": 5,
"empLevel": 1
},
{
"empId": 22,
"empName": "Max",
"empGroupId": 5,
"empLevel": 2
},
{
"empId": 68,
"empName": "Jack",
"empGroupId": 5,
"empLevel": 3
},
{
"empId": 1,
"empName": "Alex",
"empGroupId": 7,
"empLevel": 0
},
{
"empId": 38,
"empName": "Mark",
"empGroupId": 7,
"empLevel": 7
},
{
"empId": 12,
"empName": "Danny",
"empGroupId": 7,
"empLevel": 2
},
{
"empId": 1,
"empName": "Alex",
"empGroupId": 4,
"empLevel": 0
},
{
"empId": 38,
"empName": "Mark",
"empGroupId": 4,
"empLevel": 1
},
{
"empId": 55,
"empName": "Kate",
"empGroupId": 4,
"empLevel": 2
}
]
}
I want to create a JSON tree structure which will map all the employees in hierarchical manner. Such that all the common employees will appear only once and according to their levels.
So basically, empName will have a unique empId associated with it. Multiple employees can be part of employee group [indicated by empGroupId]. The empLevel indicates level of employee, 0 being top person, then 1, and so on...
For e.g, if we consider first 8 rows in table above, it contains 2 empGroupIds, 3 & 5. Alex, Sam and Max are the employees common in both groups with levels 0,1,2 respectively. Ervin and Jack are the last level at 3. And since their top 3 members are same, the final structure will have: Alex -> Sam -> Max -> [Ervin, Jack]
So below is what I want to generate,
[{
"empName":"Alex",
"empId" : 1,
"empLevel" : 0,
"children" :[{
"empName":"Sam",
"empId" : 42,
"empLevel" : 1,
"children" : [{
"empName":"Max",
"empId" : 22,
"empLevel" : 2,
"children": [{
"empName":"Ervin",
"empId" : 54,
"empLevel" : 3
},{
"empName":"Jack",
"empId" : 68,
"empLevel" : 3
}]
}]
},
{
"empName":"Mark",
"empId" : 38,
"empLevel" : 1,
"children":[{
"empName":"Danny",
"empId" : 12,
"empLevel" : 2
},{
"empName":"Kate",
"empId" : 55,
"empLevel" : 2
}]
}]
}]
So far I have created a sample program to read the JSON file and mapped the employees. However not sure how to approach the design for this tree structure. This is what I have right now,
#Data
public class Employee {
private Integer empId;
private String empName;
private Integer empGroupId;
private Integer empLevel;
}
#Data
public class EmpLevels {
private List<Employee> employees;
}
#Data
public class EmpTree {
private Integer empId;
private String empName;
private Integer empLevel;
private List<Employee> children;
}
My main method contains below stuff so far, regarding reading JSON and mapping employees,
EmpLevels empLevels = mapper.readValue(Paths.get("emp.json").toFile(), EmpLevels.class);
List<Employee> employees = empLevels.getEmployees();
System.out.println("Employees : "+employees);
How to go ahead with building the logic to generate the JSON tree structure? Do we have any libraries that can help here? Or any latest Java release feature that can help generate this one?
It is a matter of algorithm. Libraries or Java feature can't help you.
First of all, EmpTree.children should be type of List<EmpTree>.
#Data
public class EmpTree {
private Integer empId;
private String empName;
private Integer empLevel;
private List<EmpTree> children;
}
Here is a recursive function that build a EmpTree.
public EmpTree buildTree(List<Employee> employees, int empId, int empLevel) {
List<Employee> parent = employees.stream()
.filter(employee -> empId == employee.getEmpId())
.collect(Collectors.toList());
List<Integer> groups = parent.stream()
.map(Employee::getEmpGroupId)
.collect(Collectors.toList());
List<EmpTree> childTree = employees.stream()
.filter(child -> child.getEmpLevel() == empLevel + 1 && groups.contains(child.getEmpGroupId()))
.map(Employee::getEmpId)
.distinct()
.map(childEmpId -> buildTree(employees, childEmpId, empLevel + 1))
.collect(Collectors.toList());
return new EmpTree(empId, parent.get(0).getEmpName(), parent.get(0).getEmpLevel(), childTree.isEmpty() ? null : childTree);
}
Use this statement to call the function.
EmpTree root = buildTree(employees, 1, 0);
When I look at your JSON, what first comes to my mind would be:
public class Employee {
private Integer empId;
private String empName;
private Integer empGroupId;
private List<Employee> children;
}
If thats not it, maybe their is a way to generate an Object from your JSON with Jackson/ObjectMapper and find an idea their? But I don't know if Jackson can do this without a given class, but maybe it helps you while looking for a solution.
Related
Changing value of specific key changes the value of other keys as well
This is how I generate dutyList. val dutyList = ArrayList<Triple<Int, String, ArrayList<Pair<String, String>>>>() val dateShiftPair = ArrayList<Pair<String, String>>() dateList.forEach {date -> dateShiftPair.add(Pair(date, "E")) } staffList.forEach {staff -> dutyList.add(Triple(list.indexOf(staff), staff.name!!, dateShiftPair)) } And this should change the second value of the pair override fun onShiftChange(pos: Int, datePos: Int, shift: String) { val pair = Pair(staffList[pos].third[datePos].first, shift) staffListUpdated[pos].third[datePos] = pair } but instead it changes other values in pos, that is if I change staffListUpdated[0].third[0] = pair it changes staffListUpdated[1].third[0] = pair as well. I tried many ways but nothing helped. [ { "first": 0, "second": "Ralph", "third": [ { "first": "3/5", "second": "G" //change should happen here only }, { "first": "4/5", "second": "E" }, { "first": "6/5", "second": "E" } ] }, { "first": 1, "second": "Mike", "third": [ { "first": "3/5", "second": "G" //but change happens here as well. }, { "first": "4/5", "second": "E" }, { "first": "5/5", "second": "E" } ] } ]
In staffList.forEach {staff -> dutyList.add(Triple(list.indexOf(staff), staff.name!!, dateShiftPair)) } you use for every Triple the same list instance. This means if you get the list of one of your Triples and change something you change it for every Triple. A solution is to copy the list either on insertion or on modification. You also need to do a deep copy of the list. Because if you make a shallow copy of the list the instances of the Pairs are still the same and if you then change one of them you change it (again) for every Triple.
How to convert json values to keys? Basically I want to use value of one key as a "key" and value of other as value
I am writing API in java spring boot. I am consuming below json as response from another API [ { "item": "Apple", "count": 30 }, { "item": "Orange", "count": 10 }, { "item": "Guava", "count": 4 } ] I want to convert this to { "Apple": 30, "Orange": 10, "Guava": 4 } What is the simplest & efficient way to do this?
Define a class to hold an item public record Item { String item; int count; } Deserialise to a List<Item> using your favourite json library (mine is Jackson). The collect to a map: Map<String, Integer> map = list.stream().collect(toMap(Item::getItem, Item::getCount));
How to sort and filter a field which is in other indices in elasticsearch?
For example, I have a index named 'student': { "id": "100", "name": "Frank" } then there is another index named 'grade': { "id": "1" "score": 95, "studentId": "100" } how can I use one query to get a page of student and sort by score? Can I use join query to search these two indices like MySQL? This is what I want to get: { "id": "100", "name": "Frank", "score": "95" }, { ... }
Unfortunately no. Gotta normalize your data since joins are not possible in ES. More at elastic.co/guide/en/elasticsearch/guide/current/relations.html
Java 8 Lambda - filter two ArrayList
I have two lists. The first one is an ID list of regions, the second one is a node list which has nested region objects. Structure of my nodeList: ....{ "id": 6412, "name": "303000201", "description": "desc", "organization": { "id": 41065, "name": "adad", "address": null }, "region": { "id": 21, "name": "Quba rayonu", "code": "303" }, "nodeType": { "id": "WELL", "name": "Quyu", "description": null }, "location": { "id": 6412, "latitude": 41.36735554, "longitude": 48.5041245554 }} ...... And region list: { "regions": ["56", "44"] } I must filter my nodeList for region ID. I do it the old way but I want to do it with lambda expressions. How can I do it? I googled, tried, but it doesn't work :/ result= nodeList.stream() .filter(n -> regionIDList.equals(n.getRegion().getId().toString())) .collect(Collectors.toList()); Thank you in advance.
Assuming regionIDList is a List, use contains instead of equals: result = nodeList.stream() .filter(n -> regionIDList.contains(n.getRegion().getId().toString())) .collect(Collectors.toList());
parsing json using Gson which has objects with same field name
I have a json file which has following structure: { "Otions": true "Plastic": "" "Houses": { 7: { "name": "Akash Bhawan" "MemBers": 3 "children": 1 }- 8: { "name": "Ashiyana" "memBers": 4 "children": 2 }- 9: { "name": "Faruks Nest" "memBers": 5 "children": 1 }- The objects inside Houses are variable and can increase or decrease accordingly and also change names. How ever the fieds "name", "members", "children" are the only fields and will always be there i am using gson to parse #SerializedName("Otions") private String Options; #SerializedName("Plastic") private String plastics; #SerializedName("Houses") private Houses houses; i want to know if there is a way we can i can store differently named objects in a hashtable or some other way?
If you can't change the structure then you have to make house as Hashmap of int to object. For Example:- HashMap<int, Object> House; And that object will have elements like name, memBers, details.
Houses should be a map like this. private HashMap houses. Since the 3: indicates an index-structure. Gson will create the nested objects as needed.
Please make "house" as bunch of array instead of objects. { "Options": true, "Plastics": "", "house": [{ "name": "Akash Bhawan", "MemBers": 3, "children": 1 }, { "name": "Ashiyana", "memBers": 4, "children": 2 }, { "name": "Faruks Nest", "memBers": 5, "children": 1 }] }
You should do something like (as said in my comment): JSONObject jsonHouses = jsonObject.getJSONObject("Houses"); Iterator<String> keys = jsonHouses.keys(); //this will be array of "7", "8", "9" if (keys.hasNext()) { JSONObject singleHouseObject = jsonHouses.getJSONObject(keys.next()); //now do whatever you want to do with singleHouseObject. //this is the object which has "name", "MemBers", "children" }