How to generate an empty json node using jackson-java. I tried with NullNode.instance, but that returns
"totals":null
Whereas I want totals to be an empty instance.
{
"totals": {},
"orderId": "550047004",
"numberOfItems": 2
}
You should use ObjectNode. It can be created with ObjectMapper:
ObjectNode node = mapper.createObjectNode();
Related
This is similar to this question but it's a little different.
Let's say I have a json document defined like this:
[
{ "type" : "Type1",
"key1" : "value1" },
{ "type" : "Type2",
"key2" : "value2" }
]
I want to read this json document into a list of strings (List<String>). I only want to read the outer-most list into the Java List, the json objects inside the list should be left as-is inside the list. The result should be equivalent to this (I ignore newlines etc):
var myList = List.of("{\"type\": \"Type1\", \"key1\": \"value1\"}, {\"type\": \"Type2\", \"key2\": \"value2\"}")
Note that I don't want to create any DTO to hold some intermediate representation. I just want everything below the "list" to be represented "as-is".
How can I achieve this?
I'm using Jackson 2.12.1.
If you don't want to hold intermediate representation in a DTO, then one way in which the required deserialization can be achieved is:
// Create a ObjectMapper (of type com.fasterxml.jackson.databind.ObjectMapper)
ObjectMapper mapper = new ObjectMapper();
// Read the json string into a List. This will be deserialized as a collection of LinkedhashMap
List<LinkedHashMap> list = mapper.readValue(getInputString(), List.class);
//Iterate over the deserialized collection and create a JSONObject from every LinkedHashMap
List<String> result = list.stream()
.map(map -> new JSONObject(map).toString())
.collect(Collectors.toList());
This will produce:
[{"key1":"value1","type":"Type1"}, {"key2":"value2","type":"Type2"}]
Downside of this approach is, it takes a hit on performance.
I have following class that is POJO of JSON document:
public class ApiResponse {
private boolean success;
private List<ApiRecord> data;
}
I deserialize object using ObjectMapper class in the following way:
var apiResponse = mapper.readValue(target, ApiResponse.class);
I want to make Jackson treat every ApiRecord deserialization failure as not failure of whole deserialization process but instead just get a list that contains only valid parsed objects, so the wrong elements of 'data' field are acceptable (not appearing in POJO list) and not blocking the rest ones.
Any idea on how to do this?
Do one thing add one property to the mapper object.
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
var apiResponse = mapper.readValue(target, ApiResponse.class);
My solution to this problem was to write my own deserializer which in case of failure returns null for array's element and tag the field that needs to use this deserializer with #com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = SomeClass.class).
Here is an example json
{
"key1": {
"key2": {
"key3": "value3"
}
}
}
I want to get the value of key3, which is "value3"
The method findValue of JsonNode class should serve the purpose here.
so I tried the following:
final ObjectMapper jsonMapper = new ObjectMapper();
String jsonRoot = "{\"key1\":\n" + " {\"key2\":\n" + " {\"key3\":\"value3\"}\n" + " }\n" + "}";
JsonNode node = jsonMapper.convertValue(jsonRoot,JsonNode.class);
JsonNode found = node.findValue("key3");
System.out.println(found.asText());
System.out.println(found.isObject());
However, I see "found" is null.
I am unable to figure out why this failing. I also tried node.findValue("key2"). I still get null.
Thanks
There is difference in between these 2 methods
JsonNode.get() method returns null
Use JsonNode.path(String).asText() which checks if node is present or
not, if not then it returns empty string.
The convertValue function is used to convert one instance type into another instance type. It is a two step conversion process which is equivalent to first serializing given value into JSON, then binding JSON data into value of second given type.
In your example above, the first argument of convertValue is actually a JSON(represented in a string) and not an object, hence this does not work.
To make this work, you can use following methods :
Method 1 :
JsonNode node = jsonMapper.readTree(jsonRoot);
This will deserialize the json as a tree and returns the root of the tree which can be used for traversal now.
Method 2 :
JsonNode node = jsonMapper.readValue(jsonRoot, JsonNode.class);
This will deserialize the json to JsonNode object directly.
So something like this should work.(although untested)
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(new File("c:\\example.json"));
JsonNode key1Node = root.path("key1");
JsonNode key2Node = key1Node.path("key2");
access value of key 3 node
String val = key2Node.path("key3").asText();
See Reference here.
I have a new JsonNode that I created
JsonNode jNode = new ObjectCodec().createObjectNode();
with this node, how do I then add key value pairs within so that I can construct this new node with the new values? What I read in http://www.cowtowncoder.com/blog/archives/2011/08/entry_460.html mentioned about using
jNode.with("newNode").put("key1","value1");
But looking at the APIs for Jackson's JsonNode (v1.8) does not show any method as such.
These methods are in ObjectNode: the division is such that most read operations are included in JsonNode, but mutations in ObjectNode and ArrayNode.
Note that you can just change first line to be:
ObjectNode jNode = mapper.createObjectNode();
// version ObjectMapper has should return ObjectNode type
or
ObjectNode jNode = (ObjectNode) objectCodec.createObjectNode();
// ObjectCodec is in core part, must be of type JsonNode so need cast
I've recently found even more interesting way to create any ValueNode or ContainerNode (Jackson v2.3).
ObjectNode node = JsonNodeFactory.instance.objectNode();
I am changing my JSON library from org.json to Jackson and I want to migrate the following code:
JSONObject datasets = readJSON(new URL(DATASETS));
JSONArray datasetArray = datasets.getJSONArray("datasets");
Now in Jackson I have the following:
ObjectMapper m = new ObjectMapper();
JsonNode datasets = m.readTree(new URL(DATASETS));
ArrayNode datasetArray = (ArrayNode)datasets.get("datasets");
However I don't like the cast there, is there the possibility for a ClassCastException?
Is there a method equivalent to getJSONArray in org.json so that I have proper error handling in case it isn't an array?
Yes, the Jackson manual parser design is quite different from other libraries. In particular, you will notice that JsonNode has most of the functions that you would typically associate with array nodes from other API's. As such, you do not need to cast to an ArrayNode to use. Here's an example:
JSON:
{
"objects" : ["One", "Two", "Three"]
}
Code:
final String json = "{\"objects\" : [\"One\", \"Two\", \"Three\"]}";
final JsonNode arrNode = new ObjectMapper().readTree(json).get("objects");
if (arrNode.isArray()) {
for (final JsonNode objNode : arrNode) {
System.out.println(objNode);
}
}
Output:
"One"
"Two"
"Three"
Note the use of isArray to verify that the node is actually an array before iterating. The check is not necessary if you are absolutely confident in your datas structure, but its available should you need it (and this is no different from most other JSON libraries).
In Java 8 you can do it like this:
import java.util.*;
import java.util.stream.*;
List<JsonNode> datasets = StreamSupport
.stream(obj.get("datasets").spliterator(), false)
.collect(Collectors.toList())
I would assume at the end of the day you want to consume the data in the ArrayNode by iterating it. For that:
Iterator<JsonNode> iterator = datasets.withArray("datasets").elements();
while (iterator.hasNext())
System.out.print(iterator.next().toString() + " ");
or if you're into streams and lambda functions:
import com.google.common.collect.Streams;
Streams.stream(datasets.withArray("datasets").elements())
.forEach( item -> System.out.print(item.toString()) )
Is there a method equivalent to getJSONArray in org.json so that I have proper error handling in case it isn't an array?
It depends on your input; i.e. the stuff you fetch from the URL. If the value of the "datasets" attribute is an associative array rather than a plain array, you will get a ClassCastException.
But then again, the correctness of your old version also depends on the input. In the situation where your new version throws a ClassCastException, the old version will throw JSONException. Reference: http://www.json.org/javadoc/org/json/JSONObject.html#getJSONArray(java.lang.String)
Obtain an iterator by calling the JsonNode's iterator() method, and go on...
JsonNode array = datasets.get("datasets");
if (array.isArray()) {
Iterator<JsonNode> itr = array.iterator();
/* Set up a loop that makes a call to hasNext().
Have the loop iterate as long as hasNext() returns true.*/
while (itr.hasNext()) {
JsonNode item=itr.next();
// do something with array elements
}
}