Jackson Json accesing JsonNode property name - java

I have a schema like this:
{
"type" : "object",
"$schema" : "http://json-schema.org/draft-03/schema#",
"id" : "urn:jsonschema:com:vlashel:dto:UserDto",
"description" : "this is the top description",
"title" : "this is the top title",
"properties" : {
"number" : {
"type" : "integer"
"required" : true
},
"password" : {
"type" : "string"
"required" : true
}
}
I have the following code that converts this shcema draft 3 to draft 4 by removing "required", I want to collect nodes property names that have 'requred' in them. How do I do that? I don't see methods for this..
JsonNode jsonNode = jsonNodeIterator.next();
ObjectNode element;
if (jsonNode instanceof ObjectNode) {
element = (ObjectNode) jsonNode;
element.remove("required");
String propertyName = element.getPropertyName(); //I'm looking for this kind of method.
Thanks!

You can get all of the nodes that have that property by using the List<JsonNode> findParents(String fieldName), which does that this for you. From the docs:
Method for finding a JSON Object that contains specified field, within
this node or its descendants. If no matching field is found in this
node or its descendants, returns null.
I made a quick example but had to add a few characters to the JSON blob you posted as it is missing some commas and a bracket and can't be read by the ObjectMapper. It is as simple as this:
JsonNode root = mapper.readTree(SCHEMA);
List<JsonNode> required = root.findParents("required");
for (JsonNode node: required) {
Object prettyOutput = mapper.readValue(node, Object.class);
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(prettyOutput));
}
Output:
{
"type" : "integer",
"required" : true
}
{
"type" : "string",
"required" : true
}

Related

Set child node as root node using jackson

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!

How to extract item from nested list - Rest Assured

I've got this json object
{
"type" : "employee",
"columns" :
[
{
"id" : 1,
"human" :
[
{
"name" : "ANA",
"age" : "23"
},
{
"name" : "IULIA",
"age" : "22"
}
]
},
{
"id" : 2,
"human" :
[
{
"name" : "ADI",
"age" : "21"
},
{
"name" : "GELU",
"age" : "18"
}
]
}
]
}
and I need to extract the first name from each human list.
I've tried .body("columns.human.name[0]", everyItem(containsString("A"))) but it doesn't work. Any ideas?
Using JsonPath you can get all columns and all humans.
Each of JSON Object is represented as HashMap<>. If it contains only fields it's HashMap<String, String> but if contains arrays or nested JSON Objects then it is HashMap<String, Object> where Object is either another JSON Object or array.
Given the above you can use following code to get all columns and name of first human in each column:
JsonPath path = response.jsonPath();
List<HashMap<String, Object>> columns = path.getList("columns");
for (HashMap<String, Object> singleColumn : columns) {
List<HashMap<String, Object>> humans = (List<HashMap<String, Object>>) singleColumn.get("human");
System.out.println(humans.get(0).get("name"));
}
The above code will print ANA and ADI in the console.
You can store the results in List<String> for further processing
you can get all "name" from humans with jsonPath : $.columns[*].human[*].name it will give below result :
[
"ANA",
"IULIA",
"ADI",
"GELU"
]
And if you want only first "name" then you need to use josn : $.columns[*].human[0].name this will give you below result:
[
"ANA",
"ADI"
]

How to use Jackson to Marshall a list of JSON objects that contain unknown number of KV paiirs

Lets say that you have a json array that looks as follows :
[
{
"id" : 1,
"first_name" : "Jane",
"last_name" : "Doe"
},
{
"id" : 2,
"first_name" : "John",
"middle_name" : "Q",
"last_name" : "Public",
"birth_year" : 1971
},
{
"id" : 3,
"anonymous_user" : true,
"crm_id" : "abc123"
},
{
"id" : 4,
"first_name" : "Albert",
"last_name" : "Einstein",
"profession" : "Scientist",
"birth_year" : 1879,
"e_equals_mc_squared" : true
}
]
The goal is to use Jackson to marshal to POJO. My thinking is that I could have a class to contain each K,V pair .. something like :
public class myDataObject {
private String key;
private T value;
...
}
And maybe a container class for that :
public class myDataContainer {
private ArrayList<myDataObject> dataList;
...
}
My question becomes what does marshaling that look like using jackson? There is no schema for the json, each json object can have an unspecified number of K,V pairs and the list of keys is also unspecified.
Does something like this work? Is this even the right approach?
ArrayList<myDataContainer> dataList = mapper.readValue(jsonFile, new TypeReference<ArrayList<myDataContainer>() {});
What your JSON really is, is a list of maps - with Strings as keys and Objects as values.
So, using Jackson, you should be able to do:
ObjectMapper mapper = new ObjectMapper();
List<Map<String, Object>> data = mapper.readValue(json, new TypeReference<List<Map<String, Object>>>(){});

retrieve values from nested json array in mongodb

My mongo collection has entries in the following format
{
"myobj" : {
"objList" : [
{ "location" : "Texas" },
{ "location" : "Houston"},
{ "name":"Sam" }
]
},
"category" : "cat1"
}
{
"myobj" :
{
"objList" : [
{ "location" : "Tennesy" },
{ "location" : "NY"},
{ "location" : "SF" }
]
},
"category" : "cat2"
}
I want to extract the "**category**" where location is "Houston". In case of simple JSON object I have to just pass it as query like:
BasicDBObject place = new BasicDBObject();
place.put("location", "Houston");
But in case of nested JSON I don't know how to pass it as a query and get the appropriate category. ie If I pass my location as"Houston" then it should return it's appropriate category "cat1"...i hope my question is clear now....
Ok, you have your documents:
db.coll1.insert({
"myobj" : {
"objList" : [
{ "location" : "Texas" },
{ "location" : "Houston"},
{ "name":"Sam" }
]
},
"category" : "cat1"
})
and
db.coll1.insert({
"myobj" : {
"objList" : [
{ "location" : "Tennesy" },
{ "location" : "Houston"},
{ "location" : "SF" }
]
},
"category" : "cat1"
})
Now you can find what you want using the dot operator:
db.coll1.find({"myobj.objList.location": "Texas"}).pretty() will return one object which has Texas
db.coll1.find({"myobj.objList.location": "SF"}).pretty() will return one object which has SF
db.coll1.find({"myobj.objList.location": "Houston"}).pretty() will return both objects
And now I hope you will be able to write it in Java. I have never used Java, but based on this question you can do something like this. If it will not work, just look how to use dot operator in java driver for mongo:
DBCursor cursor = coll1.find(new BasicDBObject("myobj.objList.location", "Texas"));
P.S. you told, that you wanted to retrieve category. In such a way, you will need to use a projection db.coll1.find({<the query I provided}, {category: 1, _id: 0})

Jackson JsonSchema generating schema incorrectly

I'm attempting to use Jackson to generate JSON schemas from POJOs. Jackson has two ways to do this. This is the first:
ObjectMapper mapper = new ObjectMapper();
JsonSchema schema = mapper.generateJsonSchema(Entity.class);
String schemaString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(schema);
This is fine - it generates exactly what you'd expect.
{
"type" : "object",
"properties" : {
"pedigreeIds" : {
"type" : "array",
"items" : {
"type" : "string"
}
},
...
}
}
However, this approach is deprecated in favor of the second, which uses jackson-module-jsonschema:
ObjectMapper mapper = new ObjectMapper();
SchemaFactoryWrapper visitor = new SchemaFactoryWrapper();
mapper.acceptJsonFormatVisitor(mapper.constructType(Entity.class), visitor);
JsonSchema schema = visitor.finalSchema();
String schemaString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(schema);
Using this code spits out the following for the same entity class:
{
"$ref" : null,
"$schema" : null,
"disallow" : null,
"id" : null,
"required" : null,
"description" : null,
"title" : null,
"enums" : [ ],
"additionalProperties" : null,
"dependencies" : [ ],
"patternProperties" : { },
"properties" : {
"observationTime" : {
"$ref" : null,
"$schema" : null,
"disallow" : null,
...
}
}
}
Given that the first method is deprecated, I'd prefer to use the second if possible. I can't find any differences between what I'm doing and what various code snippets on the internet are doing.
Is there a configuration switch I'm missing, or something I'm simply doing wrong in the second example that causes Jackson to not generate the expected schema?

Categories