Jackson - How to Process (Deserialize) Nested JSON

Jackson - How to process (deserialize) nested JSON?

Your data is problematic in that you have inner wrapper objects in your array. Presumably your Vendor object is designed to handle id, name, company_id, but each of those multiple objects are also wrapped in an object with a single property vendor.

I'm assuming that you're using the Jackson Data Binding model.

If so then there are two things to consider:

The first is using a special Jackson config property. Jackson - since 1.9 I believe, this may not be available if you're using an old version of Jackson - provides UNWRAP_ROOT_VALUE. It's designed for cases where your results are wrapped in a top-level single-property object that you want to discard.

So, play around with:

objectMapper.configure(SerializationConfig.Feature.UNWRAP_ROOT_VALUE, true);

The second is using wrapper objects. Even after discarding the outer wrapper object you still have the problem of your Vendor objects being wrapped in a single-property object. Use a wrapper to get around this:

class VendorWrapper
{
Vendor vendor;

// gettors, settors for vendor if you need them
}

Similarly, instead of using UNWRAP_ROOT_VALUES, you could also define a wrapper class to handle the outer object. Assuming that you have correct Vendor, VendorWrapper object, you can define:

class VendorsWrapper
{
List<VendorWrapper> vendors = new ArrayList<VendorWrapper>();

// gettors, settors for vendors if you need them
}

// in your deserialization code:
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readValue(jsonInput, VendorsWrapper.class);

The object tree for VendorsWrapper is analogous to your JSON:

VendorsWrapper:
vendors:
[
VendorWrapper
vendor: Vendor,
VendorWrapper:
vendor: Vendor,
...
]

Finally, you might use the Jackson Tree Model to parse this into JsonNodes, discarding the outer node, and for each JsonNode in the ArrayNode, calling:

mapper.readValue(node.get("vendor").getTextValue(), Vendor.class);

That might result in less code, but it seems no less clumsy than using two wrappers.

Simple Jackson deserialization of nested objects

You have to create the POJO and then use the Jackson ObjectMapper API to read the JSON string to java object.

Here is the working code basing on your sample string.

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({ "info", "data" })
public class Process {

@JsonProperty("info")
private String info;
@JsonProperty("data")
private Data data;
@JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();

@JsonProperty("info")
public String getInfo() {
return info;
}

@JsonProperty("info")
public void setInfo(String info) {
this.info = info;
}

@JsonProperty("data")
public Data getData() {
return data;
}

@JsonProperty("data")
public void setData(Data data) {
this.data = data;
}

@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}

@JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}

}

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({ "id", "cars" })
public class Data {

@JsonProperty("id")
private String id;
@JsonProperty("cars")
private List<Car> cars = null;
@JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();

@JsonProperty("id")
public String getId() {
return id;
}

@JsonProperty("id")
public void setId(String id) {
this.id = id;
}

@JsonProperty("cars")
public List<Car> getCars() {
return cars;
}

@JsonProperty("cars")
public void setCars(List<Car> cars) {
this.cars = cars;
}

@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}

@JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}

}

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({ "id" })
public class Car {

@JsonProperty("id")
private String id;
@JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();

@JsonProperty("id")
public String getId() {
return id;
}

@JsonProperty("id")
public void setId(String id) {
this.id = id;
}

@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}

@JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}

}

Code to deserialize the JSON string.

import java.io.IOException;
import java.util.List;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class MainApp {

public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {

String input = "{\r\n" +
" \"info\": \"processing\",\r\n" +
" \"data\": {\r\n" +
" \"id\": \"123\",\r\n" +
" \"cars\": [\r\n" +
" {\r\n" +
" \"id\": \"1\"\r\n" +
" },\r\n" +
" {\r\n" +
" \"id\": \"2\"\r\n" +
" }\r\n" +
" ]\r\n" +
" }\r\n" +
"}";
ObjectMapper mapper = new ObjectMapper();
Process process = mapper.readValue(input, Process.class);
System.out.println(process.getInfo());
Data data = process.getData();
List<Car> cars = data.getCars();
for(Car car : cars) {
System.out.println(car.getId());
}

}

}

Hope this helps.

How to parse nested json object as it is using Jackson

Any Jason object could be mapped to Map<String, Object> and any Json List could be mapped to List<Object>. So you could parse your JSON as follows:

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModules(new JavaTimeModule());
objectReader = objectMapper.reader();
Map<String, Object> parsedJson = (Map<String, Object>)objectReader.forType(Map.class).readValue(jsonString)

Note that your JSON is a JSON object and not JSON list, so this example parses it to Map

Parsing deeply nested JSON properties with Jackson

JsonPath library allows one to select only required fields and thn you can use Jackson to convert raw data to POJO class. Example solution could look as follows:

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.jayway.jsonpath.JsonPath;

import java.io.File;
import java.util.List;
import java.util.Map;

public class JsonPathApp {

public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();

List<Map> nodes = JsonPath.parse(jsonFile).read("$..value[*].user.name");

ObjectMapper mapper = new ObjectMapper();
CollectionType usersType = mapper.getTypeFactory().constructCollectionType(List.class, User.class);
List<User> users = mapper.convertValue(nodes, usersType);
System.out.println(users);
}
}

class User {

@JsonProperty("first")
private String firstName;

@JsonProperty("last")
private String lastName;

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

@Override
public String toString() {
return "User{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
'}';
}
}

Above code prints:

[User{firstName='x', lastName='y'}]

How can I deserialize it using Java?

Change your Information class to the following (mind the Jackson annotations to match JSON properties):

@Getter
@Setter
class Information {
@JsonProperty("query_name")
String name;
@JsonProperty("query_type")
String type;
List<User> details;
}

And then you just need to use Jackson's ObjectMapper to convert your JSON String to the corresponding objects as follows:

ObjectMapper objectMapper = new ObjectMapper();
String informationJsonFromDatabase = ""; // whatever logic you need here to get the String from the database
Information information =
objectMapper.readValue(informationJsonFromDatabase, Information.class);

I have doubts regarding Integer age;, because in your JSON it is represented as a String, so I am not entirely sure that Jackson is able to do the convertion. If it is not you will need to either change the property to String age; or create a custom deserializer for User class.

Why is the Jackson Object Mapper unable to deserialize this nested JSON?

This is the correct way to implement.

Please pay attention to object point of view of all thing:

These are the pojos:

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
"start_time",
"end_time",
"value"
})
public class Datum {

@JsonProperty("start_time")
public String startTime;
@JsonProperty("end_time")
public String endTime;
@JsonProperty("value")
public Double value;

}

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
"space_id",
"channel_id",
"aggregation_type",
"pids_count",
"timezone"
})
public class Meta {

@JsonProperty("space_id")
public String spaceId;
@JsonProperty("channel_id")
public Integer channelId;
@JsonProperty("aggregation_type")
public String aggregationType;
@JsonProperty("pids_count")
public Integer pidsCount;
@JsonProperty("timezone")
public String timezone;

}

This is the wrapper root Object:

import java.util.List;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
"code",
"message",
"data",
"meta"
})
public class ExampleStack {

@JsonProperty("code")
public Integer code;
@JsonProperty("message")
public String message;
@JsonProperty("data")
public List<Datum> data = null;
@JsonProperty("meta")
public Meta meta;

}

And this is the working example:

import com.fasterxml.jackson.databind.ObjectMapper;//<--IMPORTANT!

public class TestJacksonObject {

public static void main(String[] args) {

ObjectMapper mapper = new ObjectMapper();
ExampleStack stack = null;
try {
stack = mapper .readValue( new FileInputStream(new File("C://test.json")) , ExampleStack.class);

} catch (IOException e) {
e.printStackTrace();
}

System.out.println(stack.message);//<--Do whatever you want...
.....

If it's too hard to produce all these classes, which is actually tedious I suggest you to autoproduce them online through this useful site:

Json2Pojo

Hope it helps you out!



Related Topics



Leave a reply



Submit