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!
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));
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 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.
I have this JSON object:
{
"maindrawer":
{
"enabled": true,
"actions":
[
{
"type": "Section",
"title": "Section 1"
},
{
"id": 1,
"type": "Primary",
"title": "Title 1",
"badge":
{
"enabled": false,
"value": 0,
"textColor": "#000000",
"badgeColor": "#ff0990"
},
"subActions":
[
{
"id": 1,
"type": "Primary",
"title": "Sub Title 1"
}
]
}
]
}
}
This is the code I'm using to access the badge -> textColor value:
public void loadJSONFromRaw(Context context, int id)
{
json = null;
try
{
//read and return json sting
InputStream is = context.getResources().openRawResource(id);
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
json = new String(buffer, "UTF-8");
//convert json to object
Type type = new TypeToken<Map<String, Object>>() {}.getType();
Map<String, Object> data = new Gson().fromJson(json, type);
//access maindrawer property
Map<String, Object> maindrawer = (Map<String, Object>)data.get("maindrawer");
//access actions list
List<Object> actions = (List<Object>)maindrawer.get("actions");
//return first item in the list
Map<String, Object> action = (Map<String, Object>) actions.get(1);
//return badge object
Map<String, String> badge = (Map<String, String>) action.get("badge");
//access badge -> textColor value
String textColor = badge.get("textColor");
}
catch (IOException e)
{
e.printStackTrace();
}
}
Is there a better/faster or more dynamic way to access JSON nested properties using java/android? I'm using Gson library for this task and don't mind to switch to any other solution to make it easier as this is too much of code to write just to access a single variable.
Ideally, I'm looking for something like:
String textColor = data.get("maindrawer").get("actions").get(1).get("badge").get("textColor");
Also I'm not very interested in using POJO for now.
Lastly, I'm still new to Java so I'm probably missing something here or maybe there are some limitations? anyways thanks for you help!!
Found what I need using JsonPath library. It looks like it does similar to what I need. Here's a sample code I found:
String textColor = JsonPath.parse(json).read("$.maindrawer.actions[1].badge.textColor");
Very clean and straightforward. Hopes this will save someone else's time as well.
Since you are accessing json file locally, it means you know its structure.
So instead of using -
Map<String, Object> data = new Gson().fromJson(json, type);
You can use something like this-
Map<String, MainDrawer> data = new Gson().fromJson(json, type);
where MainDrawer is a class with member variables - enabled, actions and array of another type.
That would make easier to fetch your values like using -
mainDrawer.isEnabled()
Here are two solutions without importing a new library.
Write a simple path parser:
String textColor = (String)parse(data, "maindrawer", "actions", 1, "badge", "textColor");
//...
static Object parse(Object root, Object... params) {
Object current = root;
for (Object p : params) {
if (p instanceof Number) {
current = ((List<?>)current).get(((Number)p).intValue());
} else {
current = ((Map<?,?>)current).get(p.toString());
}
}
return current;
}
Or parse and walk through Gson's JsonElement:
JsonElement root = new Gson().fromJson(json, JsonElement.class);
String textColor = root
.getAsJsonObject().get("maindrawer")
.getAsJsonObject().get("actions")
.getAsJsonArray().get(1)
.getAsJsonObject().get("badge")
.getAsJsonObject().get("textColor")
.getAsString();
You can also do this with BSON using a single line query. You have to cast the object to the type as you go down into Nested JSON objects.
//import java.util.ArrayList;
//import org.bson.Document;
Document root = Document.parse("{ \"maindrawer\" : { \"enabled\" : true, \"actions\" : [{ \"type\" : \"Section\", \"title\" : \"Section 1\" }, { \"id\" : 1, \"type\" : \"Primary\", \"title\" : \"Title 1\", \"badge\" : { \"enabled\" : false, \"value\" : 0, \"textColor\" : \"#000000\", \"badgeColor\" : \"#ff0990\" }, \"subActions\" : [{ \"id\" : 1, \"type\" : \"Primary\", \"title\" : \"Sub Title 1\" }] }] } }");
System.out.println(((String)((Document)((Document)((ArrayList)((Document)root.get("maindrawer")).get("actions")).get(1)).get("badge")).get("textColor")));
I have a JSON
{
"info": {
"info1": {
"age": "30",
"city": "New york"
},
"info2": {
"sleeping": "false"
},
"info3": {
"shopping": "false",
"eating": "Buger"
}
},
"data": [{
"name": "XYZ",
"email": "xyz#123.com"
}, {
"name": "ABC",
"email": "ABC#123.com"
}]
}
I need to make a parser that would be generic, and extract names for objects, arrays and individual key pair text.
I will be generating a query using these values.
Only "info" and "data" tags will be fixed rest all can change. We can have empty "info" or different children like "info1", "info2".... "info5" ...
Similarly, individual "info" child can have multiple children like "info1" can have 2 entries or 4 entries.
I tried using jackson library, but not able to traverse the complete json.
UPDATE: using jackson 2.7.2 (latest)
Used following code
JsonFactory factory = new JsonFactory();
ObjectMapper mapper = new ObjectMapper(factory);
JsonNode rootNode = mapper.readTree(jsonString);
Iterator<Map.Entry<String, JsonNode>> fieldsIterator = rootNode.fields();
while (fieldsIterator.hasNext()) {
Map.Entry<String, JsonNode> field = fieldsIterator.next();
System.out.println("Key: " + field.getKey() + "\tValue:" + field.getValue());
}
It iterated for the "info" and "data" key. Need to iterate over complete json.
JsonNode can be used to parse whole jsonObject. JsonNode get method can be used to traverse the given JSON.
e.g. :
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(data);
JsonNode infoNode = rootNode.get("info");
Iterator<Map.Entry<String, JsonNode>> infoFieldsIterator = infoNode.fields();
while (infoFieldsIterator.hasNext()) {
Map.Entry<String, JsonNode> field = infoFieldsIterator.next();
System.out.println("Key: " + field.getKey() + "\tValue:" + field.getValue());
}
JsonNode dataNode = rootNode.get("data");
for (int i = 0; i < dataNode.size(); i++) {
JsonNode dataNodeNum = dataNode.get(i);
Iterator<Map.Entry<String, JsonNode>> dataFieldsIterator = dataNodeNum.fields();
while (dataFieldsIterator.hasNext()) {
Map.Entry<String, JsonNode> field = dataFieldsIterator.next();
System.out.println("Key: " + field.getKey() + "\tValue:" + field.getValue());
}
}
For more info please check here.
Why not try GSON.
https://github.com/google/gson
You can use models inside models to achieve what you need.
ALL you need to do is serialise data using gson as follows
#SerializedName("info")
private infomodel info;
you can create a seperate model called info model which contains serialized name for its objects.
use the below code to get object from json
Gson gson=new Gson();
Model yourModel=gson.fromJson(<your json object as string>);