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
Find Out What Jvm Eclipse Is Running On
How to Use Bufferedreader in Java
Why Is Files.Lines (And Similar Streams) Not Automatically Closed
How to Test Abstract Class in Java with Junit
How to Extract Parameters from a Given Url
String Parsing in Java with Delimiter Tab "\T" Using Split
How to Stop Parsing Xml Document with Sax at Any Time
How to Use Jersey as Jax-Rs Implementation Without Web.Xml
How Does Double to Int Cast Work in Java
How to Retrieve a List of Available/Installed Fonts in Android
Eclipse Autocomplete (Content Assist) with Facelets (Jsf) and Xhtml
Problems with Local Variable Scope. How to Solve It
How to Compile Jrxml to Get Jasper
How Read Doc or Docx File in Java
JPA Query.Getresultlist() - Use in a Generic Way
How to Change Java Logging Console Output from Std Err to Std Out