Build tree structure from Json flat array in Java - Android - java

I have a json file with Categories of products.
Every entry of the json has :
Id : a unique category id
IdParent : the id of the parent node
...Name & ImageUrl
Sample Date:
[
{
"ImageUrl":"1-home_default/1",
"Id":1,
"IdParent":0,
"Name":"Root"
},
{
"ImageUrl":"2-home_default/2",
"Id":2,
"IdParent":1,
"Name":"Home"
},
{
"ImageUrl":"12-home_default/12",
"Id":12,
"IdParent":2,
"Name":"Fruit"
},
{
"ImageUrl":"336-home_default/336",
"Id":336,
"IdParent":2,
"Name":"Papers"
},
{
"ImageUrl":"33-home_default/33",
"Id":33,
"IdParent":2,
"Name":"Food"
},
{
"ImageUrl":"95-home_default/95",
"Id":95,
"IdParent":2,
"Name":"Meet"
},
{
"ImageUrl":"121-home_default/121",
"Id":121,
"IdParent":2,
"Name":"Bolvery"
},
{
"ImageUrl":"145-home_default/145",
"Id":145,
"IdParent":2,
"Name":"Milk Breakfast"
},
{
"ImageUrl":"189-home_default/189",
"Id":189,
"IdParent":2,
"Name":"Food OUt"
},
{
"ImageUrl":"269-home_default/269",
"Id":269,
"IdParent":2,
"Name":"Cleaning"
},
{
"ImageUrl":"305-home_default/305",
"Id":305,
"IdParent":2,
"Name":"Babies"
},
{
"ImageUrl":"401-home_default/401",
"Id":401,
"IdParent":2,
"Name":"Lifestyle"
},
{
"ImageUrl":"413-home_default/413",
"Id":413,
"IdParent":2,
"Name":"Products"
},
{
"ImageUrl":"426-home_default/426",
"Id":426,
"IdParent":2,
"Name":"Copaigns"
},
{
"ImageUrl":"23-home_default/23",
"Id":23,
"IdParent":12,
"Name":"Seeds"
},
{
"ImageUrl":"344-home_default/344",
"Id":344,
"IdParent":336,
"Name":"Mouth products"
},
{
"ImageUrl":"34-home_default/34",
"Id":34,
"IdParent":33,
"Name":"Macarna"
},
{
"ImageUrl":"103-home_default/103",
"Id":103,
"IdParent":95,
"Name":"Animals"
},
{
"ImageUrl":"127-home_default/127",
"Id":127,
"IdParent":121,
"Name":"Drinks"
},
{
"ImageUrl":"146-home_default/146",
"Id":146,
"IdParent":145,
"Name":"Milk"
},
{
"ImageUrl":"190-home_default/190",
"Id":190,
"IdParent":189,
"Name":"Electronics"
},
{
"ImageUrl":"276-home_default/276",
"Id":276,
"IdParent":269,
"Name":"Cleaning2"
},
{
"ImageUrl":"310-home_default/310",
"Id":310,
"IdParent":305,
"Name":"Kids Cleaning"
},
{
"ImageUrl":"402-home_default/402",
"Id":402,
"IdParent":401,
"Name":"Traveling"
}
]
I am trying to fill this Json file to Tree Structure like the one below:
public class Tree<T> {
private Node<T> root;
public Tree(T rootData) {
root = new Node<T>();
root.data = rootData;
root.children = new ArrayList<Node<T>>();
}
public static class Node<T> {
private T data;
private Node<T> parent;
private List<Node<T>> children;
}
}
So that when user clicks on on of the Nodes, if it has children, it will list the children, otherwise, it will open the products of that category.
I have converted the Json to List<Category>
How to convert List<Category> to Tree?

Based on the answer, which I've found here, I've done it in Java like this:
My mapped Object:
public class MyObject {
public Long objectId;
public String objectName;
public Long parentObjectId;
public MyObject(Long objectId, String objectName, Long parentObjectId) {
this.objectId = objectId;
this.objectName = objectName;
this.parentObjectId = parentObjectId;
}
}
The Node class:
public class Node {
public List<Node> Children = new ArrayList<>();
public Node Parent;
public MyObject Source;
public MyObject getSourceObject() {
return Source;
}
public void setSourceObject(MyObject Source) {
Source = Source;
}
public Node getParent() {
return Parent;
}
public void setParent(Node parent) {
Parent = parent;
}
}
How to build the Tree:
public List<Node> BuildTreeAndGetRoots(List<MyObject> actualObjects) {
Map<Long, Node> lookup = new HashMap<>();
List rootNodes = new ArrayList<Node>();
for (MyObject object : actualObjects) {
// add us to lookup
Node ourNode;
if (lookup.containsKey(object.objectId)) { // was already found as a parent - register the actual object
ourNode = lookup.get(object.objectId);
ourNode.Source = object;
} else {
ourNode = new Node();
ourNode.Source = object;
lookup.put(object.objectId, ourNode);
}
// hook into parent
if (object.parentObjectId == null || object.parentObjectId.equals(object.objectId)) { // is a root node
rootNodes.add(ourNode);
} else { // is a child row - so we have a parent
Node parentNode;
if (!lookup.containsKey(object.parentObjectId)) { // unknown parent, construct preliminary parent
parentNode = new Node();
lookup.put(object.parentObjectId, parentNode);
} else {
parentNode = lookup.get(object.parentObjectId);
}
parentNode.Children.add(ourNode);
ourNode.Parent = parentNode;
}
}
return rootNodes;
}

Related

Serialize Java Object attribute to JSON

I have an API built in Java Spring that return (using JacksonJaxbJsonProvider 2.5.5) a JSON object from this class:
public class FieldValues {
private String code;
private Object value;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
In the main object I've
#JsonRootName(value = "WorkRequest")
#XmlRootElement(name = "WorkRequest")
#JsonIgnoreProperties(ignoreUnknown = true)
public class WorkRequestDTOResponse {
private List<FieldValues> fieldValues;
public List<FieldValues> getFieldValues() {
return fieldValues;
}
public void setFieldValues(List<FieldValues> fieldValues) {
this.fieldValues = fieldValues;
}
}
But the output of the fieldValues object is this:
"fieldValues": [
{
"code": "anomaly",
"value": {
"#xsi.type": "ns3:boolean",
"$": "true"
}
},{
"code": "internal_note",
"value": {
"#xsi.type": "ns3:string",
"$": "Test text example"
}
}
]
instead what I need is this:
"fieldValues": [
{
"code": "anomaly",
"value": true
},{
"code": "internal_note",
"value": "Test text example"
}
]
This is my JSON Provider:
public class ErmesJSONProvider extends JacksonJaxbJsonProvider {
public ErmesJSONProvider() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true);
mapper.configure(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED, false);
_mapperConfig.setMapper(mapper);
_mapperConfig.getConfiguredMapper().setAnnotationIntrospector(new JacksonAnnotationIntrospector());
}
}
Trying to use a String instead an object:
public class FieldValues {
private String code;
private String value;
But if I set this value as String fieldValues.setValue("true"), the JSON output is "value": true instead "value": "true"
Likewise if I set this value as String but with an Integer fieldValues.setValue("1"), the JSON output is "value": 1 instead "value": "1"
If I print the return object using ObjectMapper I've the right JSON:
String payload = new ObjectMapper().writeValueAsString(requestResult)
but if I return a Response like this:
return Response.status(Response.Status.CREATED).entity(new GenericEntity<RequestResult>(requestResult){}).build()
it return the wrong JSON.
I can't understand why 😥
Someone can help me? Thanks.

Dynamic Json Formation

I am reading one file through which I need to create a specific JSON structure to pass it on UI, but I am unable to write a code for JSON formation. So I created one Java class initialized my class, tried a lot but still now able to find a way, how to form it. Here is my main Java class where I am adding an element like this
public class DynamicListForming {
public static void main(String[] args) {
NodeInfo cab = new NodeInfo("saurabh", "South");
NodeInfo cab1 = new NodeInfo("South", "ZONE1");
NodeInfo cab2 = new NodeInfo("ZONE1", "Street-1");
NodeInfo cab3 = new NodeInfo("ZONE1", "Street-2");
NodeInfo cab4 = new NodeInfo("ZONE1", "Street-3");
NodeInfo cab5 = new NodeInfo("ZONE1", "Street-4");
List<NodeInfo> NodeInfos = new LinkedList<NodeInfo>();
NodeInfos.add(cab);
NodeInfos.add(cab1);
NodeInfos.add(cab2);
NodeInfos.add(cab3);
NodeInfos.add(cab4);
NodeInfos.add(cab5);
}
}
My NodeInfo class looks like this
public class NodeInfo {
private String nodeName;
private String parentName;
private List<NodeInfo> children;
public NodeInfo(String parentName, String nodeName) {
super();
this.parentName = parentName;
this.nodeName = nodeName;
}
public String getNodeName() {
return nodeName;
}
public void setNodeName(String nodeName) {
this.nodeName = nodeName;
}
public String getParentName() {
return parentName;
}
public void setParentName(String parentName) {
this.parentName = parentName;
}
public List<NodeInfo> getChildren() {
return children;
}
public void setChildren(List<NodeInfo> children) {
this.children = children;
}
}
I need to form JSON structure like below
"nodeInfo": {
"name": "saurabh",
"children": [
{
"name": "SOUTH",
"children": [
{
"name": "Zone-1",
"children": [
{
"name": "Street-1"
},
{
"name": "Street-2"
},
{
"name": "Street-3"
},
{
"name": "Street-4"
}
]
}
]
}
]
}
Any suggestion on how to form this type of JSON structure, I struggled a lot and unable to find a way to dynamically create a list and object to form this structure.

Aggregation results cannot properly map to Java object

I'm trying to get schedules data from mongoDb.
I created the appropriate aggregation and tried to convert it within Spring Framework.
db.theaters.aggregate([
{ $match: { 'city_id': <someCityId>, 'theatreRooms.schedules.spectacle_id': <someSpecId> } },
{ $unwind: '$theatreRooms' },
{ $unwind: '$theatreRooms.schedules' },
{ $group: { _id: { name: '$name', room: '$theatreRooms.name' }, schedules: { $addToSet: '$theatreRooms.schedules.time' } } },
{ $group: { _id: '$_id.name', schedules: { $addToSet: { room: '$_id.room', schedules: '$schedules' } } } }
])
I've created properly match and unwind operations. But I've got problem with first group operation.
It seems that the operation is well interpreted, but for some reason I am not able to properly map the _id object.
Here is my code example:
public class TheaterProject {
private TheaterId _id;
private List<String> schedules;
public TheaterId get_id() {
return _id;
}
public void set_id(TheaterId _id) {
this._id = _id;
}
public List<String> getSchedules() {
return schedules;
}
public void setSchedules(List<String> schedules) {
this.schedules = schedules;
}
}
public class TheaterId {
#Field("name")
private String name;
#Field("room")
private Integer room;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getRoom() {
return room;
}
public void setRoom(Integer room) {
this.room = room;
}
}
public Document getRawSchedules(String cityId, String spectaclesId){
MatchOperation match = Aggregation.match(Criteria.where("city_id").is(cityId).and("theatreRooms.schedules.spectacle_id").is(spectaclesId));
UnwindOperation theaterUnwind = Aggregation.unwind("theatreRooms");
UnwindOperation schedulesUnwind = Aggregation.unwind("theatreRooms.schedules");
GroupOperation firstGroup = Aggregation.group(Fields.from(
Fields.field("name", "name"),
Fields.field("room", "theatreRooms.name")))
.addToSet("theatreRooms.schedules.time").as("schedules");
Aggregation agg = Aggregation.newAggregation(match,theaterUnwind,schedulesUnwind,firstGroup);
Document theaters = mongoTemplate.aggregate(agg, Theater.class, TheaterProject.class).getRawResults();
return theaters;
}
public List<TheaterProject> getSchedules(String cityId, String spectaclesId){
MatchOperation match = Aggregation.match(Criteria.where("city_id").is(cityId).and("theatreRooms.schedules.spectacle_id").is(spectaclesId));
UnwindOperation theaterUnwind = Aggregation.unwind("theatreRooms");
UnwindOperation schedulesUnwind = Aggregation.unwind("theatreRooms.schedules");
GroupOperation firstGroup = Aggregation.group(Fields.from(
Fields.field("name", "name"),
Fields.field("room", "theatreRooms.name")))
.addToSet("theatreRooms.schedules.time").as("schedules");
Aggregation agg = Aggregation.newAggregation(match,theaterUnwind,schedulesUnwind,firstGroup);
List<TheaterProject> theaters = mongoTemplate.aggregate(agg, Theater.class, TheaterProject.class).getMappedResults();
return theaters;
}
When I've invoked method getSchedules which return mapped objects, _id field is equal to null.
[
{
"_id": null,
"schedules": [
"5:15"
]
},
{
"_id": null,
"schedules": [
"6:55",
"4:35",
"10:15"
]
}
]
But when I've invoked getRawSchedules which used getRawResults it's looking properly.
{
"results": [
{
"_id": {
"name": "Pinokio",
"room": 2
},
"schedules": [
"5:15"
]
},
{
"_id": {
"name": "Roma",
"room": 1
},
"schedules": [
"6:55",
"4:35",
"10:15"
]
}
]
}
I don't have any idea why it's working like that.
I didn't find any information about this problem in the documentation and here. But I have a solution. You may just rename the field from _id to something else. theaterId for example. I don't know all requirements for your issue but you may do it just on mapping level.
Fix the mapping
import org.springframework.data.mongodb.core.mapping.Field;
import java.util.List;
public class TheaterProject {
#Field("theaterId")
private TheaterId _id;
private List<String> schedules;
public TheaterId get_id() {
return _id;
}
public void set_id(TheaterId _id) {
this._id = _id;
}
public List<String> getSchedules() {
return schedules;
}
public void setSchedules(List<String> schedules) {
this.schedules = schedules;
}
}
But it requires additional projection step
public List<TheaterProject> getSchedules(String cityId, String spectaclesId){
...
GroupOperation firstGroup = Aggregation.group(Fields.from(
Fields.field("name", "name"),
Fields.field("room", "theatreRooms.name")))
.addToSet("theatreRooms.schedules.time").as("schedules");
ProjectionOperation projection = Aggregation.project(Fields.from(
Fields.field("theaterId", "_id"),
Fields.field("schedules", "schedules")));
Aggregation agg = Aggregation.newAggregation( ... ,firstGroup, projection);
List<TheaterProject> theaters = mongoTemplate.aggregate(agg, "collectionName", TheaterProject.class).getMappedResults();
return theaters;
}

Jackson Serialize List Entities (add field to root list)

this my entity
#Entity
public class Product extends AbstractBaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Type(type = "objectid")
private String id;
private String title;
my resources
#Path(value = ApiConstant.Urls.PRODUCTS)
public class ProductResource {
#Inject
private ProductService productService;
#GET
#Path(value = ApiConstant.Urls.PRODUCTS)
#Produces(value = MediaType.APPLICATION_JSON)
public List getProducts(){
return productService.findAll();
}
my json response
[ {
"id" : "596b6a02f70a0878590bcf08",
"title" : "test1",
"description" : "description test 1"
}, {
"id" : "596b6b00f70a087b72d377eb",
"title" : "test1",
"description" : "description test 1"
}, {
"id" : "596b6b75f70a087d40f580d5",
"title" : "test1",
"description" : "description test 1"
} ]
I want to create a count field that counts the items in the list
like this and add the list to the results field
{
"count": 3,
"results": [
{
"id" : "596b6a02f70a0878590bcf08",
"title" : "test1",
"description" : "description test 1"
}, {
"id" : "596b6b00f70a087b72d377eb",
"title" : "test1",
"description" : "description test 1"
}, {
"id" : "596b6b75f70a087d40f580d5",
"title" : "test1",
"description" : "description test 1"
} ],
}
I want to serialize the Product List returned by jpa persistence
You can use the following class to include a count along with a list of Product entities:
public class ResultList {
private int count;
#JsonProperty("results") private List<Product> products;
public List<Product> getProducts() {
return products;
}
public void setProducts(List<Product> products) {
this.products = Objects.requireNonNull(products, "products");
this.count = products.size();
}
public int getCount() {
return count;
}
}
Class with generics type
public class ResultList<T> {
private int count;
#JsonProperty("results")
private List<T> items;
public List<T> getItems() {
return items;
}
public void setItems(List<T> items) {
this.items = Objects.requireNonNull(items, "items");
this.count = items.size();
}
public int getCount() {
return count;
}
}
ProductResource
#GET
#Path(value = ApiConstant.Urls.PRODUCTS)
#Produces(value = MediaType.APPLICATION_JSON)
public ResultList getProducts(){
List products = productService.findAll();
ResultList result = new ResultList<Product>();
result.setItems(products);
return result;
}
Thanks #ck1

Using Jackson for (De)serialization of Directed Graphs

I have these three classes to represent a directed Graph (for Finite State Machines):
public class Graph {
public Set<Node> nodes;
public Graph(#JsonProperty("nodes") Set<Node> nodes) {
this.nodes = nodes;
}
}
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "name")
public class Node {
public String name;
public Set<Edge> edges;
public Node(final String name, final Set<Edge> edges) {
this.edges = edges;
this.name = name;
}
public Node(#JsonProperty("name") String name) {
this(name, new HashSet<Edge>());
}
}
public class Edge {
public String name;
public Node successor;
public Edge(#JsonProperty("name") String name,
#JsonProperty("successor") Node successor) {
this.name = name;
this.successor = successor;
}
I want to map them to a JSON Object with help of Jackson 2.3.3
The serialization of a simple Graph works flawlessly without infinite backwards references:
{
"nodes" : [ {
"name" : "Z1",
"edges" : [ {
"name" : "0",
"successor" : {
"name" : "Z0",
"edges" : [ {
"name" : "0",
"successor" : "Z0"
}, {
"name" : "1",
"successor" : "Z1"
} ]
}
}, {
"name" : "1",
"successor" : "Z1"
} ]
}, "Z0" ]
}
But when I want to deserialize this exact JSON, Jackson is giving me an error:
com.fasterxml.jackson.databind.JsonMappingException: Could not resolve Object Id
[Z0] (for [simple type, class graph.Node]) --
unresolved forward-reference? (through reference chain:
graph.Node["edges"]->graph.Node["edges"])
I can see from that, that node Z0 can't be referenced because it is still "under construction", so how do I tell Jackson that it has to construct the nodes first and then add the references in the edges?

Categories

Resources