I have table in mysql like this
Parent | Child
LevelOne LevelTwo
LevelOne LevelThree
LevelTwo LevelFour
LevelTwo LevelFive
LevelFour LevelSix
I have stored them in an ArrayList like this
LevelOne | LevelTwo
LevelOne |LevelThree
LevelTwo | LevelFour
LevelTwo |LevelFive
LevelFour |LevelSix
And I m trying to convert this into JSON. This is what I have tried so far -
for(String v : values){
String p = v.substring(0,v.indexOf("|"));//Parent
String c = v.substring(v.indexOf("|")+1);//Child
ObjectNode objectNode1 = mapper.createObjectNode();
objectNode1.put("Parent", p);
objectNode1.put("Children",c);
arrayNode.add(objectNode1);
}
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(arrayNode));
However this is wrong because it prints like this
[ {
"Folder" : "LevelOne",
"Folder" : "LevelTwo"
}, {
"Folder" : "LevelOne",
"Folder" : "LevelThree"
}, {
"Folder" : "LevelTwo",
"Folder" : "LevelFour"
}, {
"Folder" : "Horror",
"Folder" : "Werewolf"
}, and so on.
Instead of
{
"folder": "LevelOne",
"subfolders": [
{
"folder": "LevelTwo",
"subfolders": [
{
"folder": "LevelFour",
"subfolders": [
{
"folder": "LevelSix"
}
]
},
{
"folder": "LevelFive"
}
]
},
{
"folder": "LevelThree"
}
]
}
Please can you advise on how to format it like this?
So here is the solution to the problem you are trying to solve.
It's although not optimised and can be refactored.
Also, it doesn't handle the use-case where there exists two parents for same node.
public static void main(String[] args) throws JsonProcessingException {
ArrayList<String> values = new ArrayList<>();
values.add("LevelOne | LevelTwo");
values.add("LevelOne |LevelThree");
values.add("LevelTwo | LevelFour");
values.add("LevelTwo |LevelFive");
values.add("LevelFour |LevelSix");
ObjectMapper mapper = new ObjectMapper();
ArrayNode arrayNode = new ArrayNode(JsonNodeFactory.instance);
for(String value : values){
String parent = value.substring(0,value.indexOf("|")).trim();
if(!arrayNode.findValuesAsText("folder").contains(parent)) {
buildNode(values, mapper, arrayNode, parent);
}
}
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(arrayNode));
}
//THIS WILL START BUILDING THE NODE
private static void buildNode(ArrayList<String> values, ObjectMapper mapper, ArrayNode arrayNode, String parent) {
ObjectNode rootNode = mapper.createObjectNode();
rootNode.put("folder", parent);
ArrayNode arrayNodeSubFolder = getSubFolders(values, mapper, parent);
if(arrayNodeSubFolder.size() != 0)
rootNode.put("subfolders", arrayNodeSubFolder);
arrayNode.add(rootNode);
}
//THIS WILL SCAN AND TRIGGER ADDING SUB NODE
private static ArrayNode getSubFolders(ArrayList<String> values, ObjectMapper mapper, String parent) {
ArrayNode arrayNode = new ArrayNode(JsonNodeFactory.instance);
for (String val : values) {
String currentParent = val.substring(0,val.indexOf("|")).trim();//Parent
if(currentParent.equals(parent)) {
String child = val.substring(val.indexOf("|") + 1).trim();//Child
buildNode(values, mapper, arrayNode, child);
}
}
return arrayNode;
}
Also, i would recommend use of better data structure to represent the input data. However, this code is specifically as per your use-case
In the first place, I'd suggest creating the tree structure already in the mysql, i.e. save the parent reference instead of child in a separate column. This eliminates redundancy:
FolderName | Parent
-------------------------
"LevelOne" | null
"LevelTwo" | "LevelOne"
"LevelThree"| "LevelOne"
"LevelFour" | "LevelTwo"
"LevelFive" | "LevelTwo"
"LevelSix" | "LevelFour"
After you load the entities into array list (6 elements), you can sort them according to the "parent" field (the order would be actually the same as in the table above. Lastly, when you iterate over the collection, you iterate the tree from the top to bottom, so in each iteration, create a node and add it to corresponding parent object. I would strongly suggest using Objects instead of strings as a representation of the tuple.
Related
I'm a new user of fasterxml.jackson lib. I need an help to undestand how use that lib.
I have an array generated from a string, that I need to include into a root node, but I didn't understand how do that.
My code is:
class RigaAnamnesi {
int codRiga;
String testo;
boolean domandaSiNo;
Integer sub = 0;
String spiegazione = "Spiegazione di prova";
}
ArrayList<RigaAnamnesi> vArray = fillArray();
String json = mapper.writeValueAsString(vArray);
JsonNode parsedJson = mapper.readTree(json);
ArrayNode outerArray = mapper.createArrayNode();
ObjectNode outerObject = mapper.createObjectNode();
outerObject.putPOJO("domande", parsedJson);
outerArray.add(outerObject);
File f = Paths.get("anamnesi.json").toFile();
mapper.writeValue(f, outerArray);
and my output is:
{
"domande": [
{
"codRiga": 1,
"testo": "Malattie del sangue e problemi di coagulazione_",
"domandaSiNo": true,
"sub": 0,
"spiegazione": "Spiegazione di prova"
},
{
"codRiga": 2,
"testo": "Malattie endocrine (es.Diabete, Osteoporosi)___ ",
"domandaSiNo": true,
"sub": 0,
"spiegazione": "Spiegazione di prova"
},
{
"codRiga": 3,
"testo": "Malattie cardiache e vascolari_",
"domandaSiNo": true,
"sub": 0,
"spiegazione": "Spiegazione di prova,"
},
...
but I need to include into a new root:
{
"anamnesi": {
"domande": [
{
"codRiga": 1,
"testo": "Malattie del sangue e problemi di coagulazione_",
"domandaSiNo": true,
"sub": 0,
"spiegazione": ""
},
....
The Jackson ObjectNode represents a JSON object {}. To add properties, you can either call set(String, JsonNode) which adds another node (object, array, or simple value) or you can call putPOJO(String, Object). POJO stands for Plain Old Java Object and this call will interpret your List<RigaAnamnesi> class as an array of JSON objects.
To get to your desired structure, you do not need to print the array to a string first. Instead, you can create two objects and use putPOJO followed by set:
List<RigaAnamnesi> vArray = ...;
ObjectNode domande = mapper.createObjectNode();
// use putPOJO to make sure jackson converts the POJO to JsonNodes
domande.putPOJO("domande", vArray);
// this is the parent we will print
ObjectNode anamnesi = mapper.createObjectNode();
// use set, since domande already is a JsonNode
anamnesi.set("anamnesi", domande);
System.out.println(mapper.writeValueAsString(anamnesi));
The question is quite simple:
From this:
{
"categoryId":"some_id",
"properties": {
"id": "braja_de_nana",
"displayName": "test",
"longDescription": "<p>TESTE</p>",
"active": true,
"attributes": [
{
"name": "made",
"value": "THIS_BECOMES_A_NODE_VALUE",
"property": "THIS_BECOMES_A_NODE_NAME"
},
{
"name": "made",
"value": "THIS_BECOMES_A_NODE_VALUE_2",
"property": "THIS_BECOMES_A_NODE_NAME_2"
}
]
}
}
UPDATE
This should be the result:
It means that every array element of 'attributes' should become a new root node.
set 'property' from 'attributes' as the object node name.
set 'value' from 'attributes' as the object node value.
{
"categoryId":"some_id",
"THIS_BECOMES_A_NODE_VALUE":"THIS_BECOMES_A_NODE_NAME",
"THIS_BECOMES_A_NODE_NAME_2":"THIS_BECOMES_A_NODE_VALUE_2"
"properties": {
"id": "braja_de_nana",
"displayName": "test",
"longDescription": "<p>TESTE</p>",
"active": true
}
}
This is a challenge for me.
I can set new nodes into the root node.
Already got a map from 'attributes' and then tried to iterate them with forEach in order to put the result into one single node, but instead as shown I have to take the 'property' set it to the object name´s key, then get the value and set to its value.
UPDATE 2
#Override
public String toOccProductDTO(ProcessProductDTO processProductDTO) throws JsonProcessingException {
OccProductDTO occProductDTO = OccProductDTO.builder()
.categoryId(processProductDTO.getCategoryId())
.productType(processProductDTO.getCategoryId())
.properties(toOccProductPropertiesDTO(processProductDTO))
.build();
toOccProductPropertiesDTO(processProductDTO);
String tree = mapper.writeValueAsString(occProductDTO);
JsonNode root = mapper.readTree(tree);
JsonNode attributesNodeArray = ((ObjectNode) root.get("properties"))
.remove("p_specs");
Iterator<JsonNode> arrayNodes = attributesNodeArray.iterator();
while (arrayNodes.hasNext()) {
JsonNode node = arrayNodes.next();
root = ((ObjectNode)root).set(node.get("value").asText(), node.get("property"));
}
System.out.println(root.toPrettyString());
return null;
}
I got an: arrayNodes: Collection$EmptyIterator at that line.
Am I doing something wrong?
If you are trying to the attributes to the root node, you can remove that node and add its fields to the root.
The "attributes" node is an array with length 1, so you have to get the first element of the array to get the attribute fields.
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(json);
JsonNode attributesNodeArray = ((ObjectNode) root.get("properties"))
.remove("attributes");
JsonNode attributesNode = attributesNodeArray.get(0);
Iterator<String> fieldNames = attributesNode.fieldNames();
while (fieldNames.hasNext()) {
String name = fieldNames.next();
root = ((ObjectNode)root).set(name, attributesNode.get(name));
}
System.out.println(root.toPrettyString());
Output:
{
"categoryId" : "some_id",
"properties" : {
"id" : "braja_de_nana",
"displayName" : "test",
"longDescription" : "<p>TESTE</p>",
"active" : true
},
"name" : "made",
"value" : "some value",
"property" : "some_value"
}
UPDATE
For the updated question, you can do the following:
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(json);
JsonNode attributesNodeArray = ((ObjectNode) root.get("properties"))
.remove("attributes");
Iterator<JsonNode> arrayNodes = attributesNodeArray.iterator();
while (arrayNodes.hasNext()) {
JsonNode node = arrayNodes.next();
root = ((ObjectNode)root).set(node.get("value").asText(), node.get("property"));
}
System.out.println(root.toPrettyString());
Output:
{
"categoryId" : "some_id",
"properties" : {
"id" : "braja_de_nana",
"displayName" : "test",
"longDescription" : "<p>TESTE</p>",
"active" : true
},
"THIS_BECOMES_A_NODE_VALUE" : "THIS_BECOMES_A_NODE_NAME",
"THIS_BECOMES_A_NODE_VALUE_2" : "THIS_BECOMES_A_NODE_NAME_2"
}
Maybe it was more complex than expected.
It turns out that I solved the problem with Oboe´s help. Although he missed some points through his implementation plus some changes I could achieve the goal.
//Converts the parsed objects into Json String
String tree = mapper.writeValueAsString(occProductDTO);
//Reads the json string to JsonNode in order to manipulate it
JsonNode root = mapper.readTree(tree);
//Sets the chosen node where the new nodes should be created
JsonNode properties = root.path("properties");
//maps the two attribs needed
Map<String, String> attribs = processProductDTO.getProductDTO().getAttributes().stream()
.collect(Collectors.toMap(AttributeDTO::getProperty, AttributeDTO::getValue));
//Converts each attrib into a String list
List<String> props = attribs.entrySet().stream()
.sorted(Comparator.comparing(Map.Entry<String, String>::getValue).reversed())
.map(Map.Entry::getKey)
.collect(Collectors.toList());
List<String> names = attribs.entrySet()
.stream() .sorted(Comparator.comparing(Map.Entry<String,String>::getValue).reversed())
.map(Map.Entry::getValue)
.collect(Collectors.toList());
//iterates over the two lists adding the attribs to their corresponding position
Iterator<String> arrayNodes = props.listIterator();
Iterator<String> arrayNodes2 = names.listIterator();
while (arrayNodes.hasNext()) {
String node = arrayNodes.next();
String node2 = arrayNodes2.next();
properties = ((ObjectNode)properties).put(node, node2);
}
return mapper.writeValueAsString(root);
}
In the end, instead of passing a java object via #Post, I´m passing a json String by using "consumes = application/json, produces = "application.json"
That´s it!
Maybe it could be achieved and better implemented with java 8 stream, but for now it works.
Suggestion to improve the code are welcome!
I have a JSON node-like below. The structure of JsonNode will change dynamically.
Now I want to replace/update the value of a particular key.
Sample JSON One
{
"company": "xyz",
"employee": {
"name": "abc",
"address": {
"zipcode": "021566"
}
}
}
Sample JSON Two
{
"name": "abc",
"address": {
"zipcode": "021566"
}
}
I want to replace the zip code value 021566 to 566258. I know key name (zipcode), old and new zip code value but I don't know the path of zip code. I tried multiple ways using com.fasterxml.jackson.databind.JsonNode - put, replace
Is there any way to update in java?
JsonNodes are immutable, but you can find the value you want from a JsonNode, cast the original to an ObjectNode, replace the value, then cast that back to a JsonNode. It's a little odd, I know, but it worked for me.
public static void findAndReplaceJsonNode throws JsonProcessingException {
String jsonOne = "{ \"company\" : \"xyz\", \"address\" : { \"zipcode\" : \"021566\", \"state\" : \"FL\" } }";
String jsonTwo = "{ \"company\" : \"abc\", \"address\" : { \"zipcode\" : \"566258\", \"state\" : \"FL\" } }";
ObjectMapper mapper = new ObjectMapper();
JsonNode nodeOne = mapper.readTree(jsonOne);
JsonNode nodeTwo = mapper.readTree(jsonTwo);
JsonNode findNode = nodeTwo.get("address");
ObjectNode objNode = (ObjectNode) nodeOne;
objNode.replace("address", findNode);
nodeOne = (JsonNode) objNode;
System.out.println(nodeOne);
}
Output:
{"company":"xyz","address":{"zipcode":"566258","state":"FL"}}
Disclaimer: While I do some JSON parsing and processing on a regular basis, I certainly wouldn't say that I'm adept at it or tree traversals with them. There is more than likely a better way to find the value of a child in a JsonNode than by taking the entire child as a node, replacing it's one value and then replacing the node. This should work for you in a pinch, though :)
I currently have a class with several attributes describing a certain type of object in my system. This class is known as EnrollmentInfo.
I also have a hashmap that is structured as follows;
HashMap<EnrolmentInfo, List<EnrolmentInfo>> devices = new HashMap<>();
As it can be seen, the value properties in this hashmap contain an ArrayList of the EnrollmentInfo class type.
To provide some context, this hashmap is used to hold the parent nodes and associated child nodes of a tree structure as key, value pairs.
I generated this hashmap by traversing and extracting details from a child/parent table such as the following:
Child : Parent
1 : 0
2 : 0
3 : 2
4 : 0
5 : 4
6 : 4
7 : 1
8 : 6
The code for extracting the parents and children and putting them into the HashMap is as follows:
// Extracts the parents and assigns them to the key values
for (EnrolmentInfo enrolmentInfo : enrolmentInfos) {
Integer nodeParentId = enrolmentInfo.getParentId();
EnrolmentInfo parentEnrolmentInfo = dms.getDevice(nodeParentId).getEnrolmentInfo();
devices.put(parentEnrolmentInfo, new ArrayList<EnrolmentInfo>());
}
// Extracts the children and assigns them to the children arraylist of each associated parent.
for (EnrolmentInfo enrolmentInfo : enrolmentInfos) {
int nodeId = enrolmentInfo.getId();
Integer parentId = enrolmentInfo.getParentId();
EnrolmentInfo nodeEnrolmentInfo = dms.getDevice(nodeId).getEnrolmentInfo();
for (Map.Entry<EnrolmentInfo, List<EnrolmentInfo>> parentDevice : devices.entrySet()) {
if (parentDevice.getKey().getId() == parentId) {
parentDevice.getValue().add(nodeEnrolmentInfo);
break;
}
}
}
My issue now is to compose this hashmap into an actual tree structure such that it can be compiled into a human-readable form via a JSON library.
More specifically how can a nested tree structure be generated based on the HashMap mentioned above?
EDIT:
Shown below is an example structure of the kind of JSON format I'm expecting at the end.
{
"id" : 0,
"children" : [
{
"id" : 1,
"children" : [
{
"id" : 7,
"children" : []
}
]
},
{
"id" : 2,
"children" : [
{
"id" : 3,
"children" : []
}
]
},
{
"id" : 4,
"children" : [
{
"id" : 5,
"children" : []
},
{
"id" : 6,
"children" : [
{
"id" : 8,
"children" : []
}
]
}
]
}
]
}
EDIT:
So far I've created a bean class that is as follows:
public class DeviceHierarchyNode implements Serializable {
#ApiModelProperty(name = "id", value = "ID of the node generated. Same as Device ID",
required = true)
private int id;
#ApiModelProperty(name = "label", value = "Device name as suggested by the user.",
required = true)
private String label;
#ApiModelProperty(name = "children", value = "List of child devices associated with device if any",
required = true)
private List<DeviceHierarchyNode> children;
My plan is to use this to create the final nested structure.
Warning: hacky.
Could you create a node type that wraps your underlying:
public class EnrolmentInfoNode {
private EnrolmentInfo info;
private List<EnrolmentInfoNode> children;
public EnrolmentInfoNode(EnrolmentInfo contents) {
this.info = contents;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + info.getId();
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
EnrolmentInfoNode other = (EnrolmentInfoNode) obj;
if (info.getId() != other.info.getId())
return false;
return true;
}
public void addChild(EnrolmentInfoNode child) {
if (children == null) {
children = new ArrayList<>();
}
children.add(child);
}
}
And then re-map thus:
Map<EnrolmentInfo, EnrolmentInfoNode> nodeMap = new HashMap<>();
for (Entry<EnrolmentInfo, List<EnrolmentInfo>> entry : map.entrySet()) {
for (EnrolmentInfo child : entry.getValue()) {
EnrolmentInfoNode childNode = nodeMap.computeIfAbsent(child, EnrolmentInfoNode::new);
nodeMap.computeIfAbsent(entry.getKey(), EnrolmentInfoNode::new)
.addChild(childNode);
}
}
Assuming you know node 0 is the parent:
String json = new GsonBuilder().setPrettyPrinting()
.create()
.toJson(nodeMap.get(enrolmentInfo0));
System.out.println(json);
If you don't, you can add a "parentNode" field to the EnrolmentInfoNode and then scan the node map to find the first one that has a null parent (therefore, root), and you're off to the races.
I'm trying to create a JSON document by using Jackson. The hierarchy goes as follows:
Event:
class Event {
private String name = "";
private Set<Integer> admin = new HashSet<>();
private List<House> houseList = new ArrayList<>();
}
House:
class House {
private List<OG> OGList = new ArrayList<>();
private int score = 0;
private String name = "";
}
Group:
class OG {
private int score = 0;
private int id = 0;
}
Every event might comprises of a set number of houses, which in turn comprises of a set number of groups. Each house and group has a score modifier as well.
Currently, this is how I print the JSON document using the pretty print method:
ObjectMapper mapper = new ObjectMapper();
File f = new File("./db/" + dir);
if (f.exists() && !f.isDirectory()) {
return "This event name is taken. Please try again.";
}
try {
mapper.writeValue(f, event);
// Convert object to JSON string and pretty print
String jsonInString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(event);
System.out.println(jsonInString);
}
}
The resulting output is pretty ugly:
{
"name" : "test",
"admin" : [ 423766405 ],
"houseList" : [ {
"score" : 0,
"name" : "first",
"oglist" : [ {
"score" : 0,
"id" : 0
}, {
"score" : 0,
"id" : 1
}, {
"score" : 0,
"id" : 2
} ]
..
}
Is there a better way to format the output, for example:
name:
test
admin:
a
b
c
houses:
name:
first
group:
1
..
It appears like you want to output YAML, not JSON.
This answer shows how simple it is to write YAML output to a file using Jackson.
This answer shows how to read a YAML file, modify it's contents, and save it back out again.