Inner object deserialization with Jackson - java

I have a json
{
"params": [
{
"key": "path",
"options": {
"string": {
"prefix": "test_pref"
}
},
"default": {
"url": ""
}
}
]}
I have the following POJO class, where i want to map inner objects like option.string.prefix in json to prefix in Params POJO class.
#Data
#Accessors(chain = true)
public class Data {
private List<Params> params;
}
#Data
#Accessors(chain = true)
public class Params {
private String key;
#JsonProperty("options.string.prefix")
private String prefix;
#JsonProperty("default.url")
private String url;
}
Is there any Jackson annotation that helps me do this without #JsonProperty?

The is #JsonGetter which is an alternative to #JsonProperty You can read a very nice article on the topic here: Jackson Annotation Examples

Related

How do i ignore a specific field from an list in JSON response

I am trying to ignore a specified field from a list during deserialization. I am not sure how do i do that for a field that sits inside a list. Below is my json and response class
Sample json
{
"key": {
"rowKey": "123"
},
"names": [
{
"firstName": "JON ",
"firstNameFormatted": "JON"
}
]
}
Response class
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
#ToString
public class Data {
private Map<String,Object> key;
private List<Map<String,Object>> names;
}
Here i would like to ignore
firstNameFormatted
from my json response but i am not sure how to do that using jackson for a field that is inside a list ?
Jackson has a solution for that. Simply use #JsonIgnore
You can see it in the example below
#JsonIgnore
public String getPassword() {
return password;
}
Now the password information won’t be serialized to JSON.
Also you can try with #JsonIgnoreProperties

Json schema with anyOf fields to POJO

I wonder what's the recommended way to generate POJOs for Json schemas with "anyOf" fields?
For example, given the following json schemas:
hobby.json
{
"anyOf": [
{ "type": {"$ref": "./exercise.json" } },
{ "type": {"$ref": "./music.json" } }
]
}
exercise.json
{
"type": "object"
"properties" {
"hobbyType": {"type": "string"}
"exerciseName": { "type": "string" },
"timeSpent": { "type": "number" },
"place": { "type": "string" }
}
}
music.json
{
"type": "object"
"properties" {
"hobbyType": {"type": "string"}
"instrument": { "type": "string" },
"timeSpent": { "type": "number" }
}
}
How could I generate a POJO for Hobby.java with Jackson?
I think there are two approaches that seem natural:
One would be to generate a class hierarchy Hobby with the common field timeSpent and Music / Exercise being subclasses with their specific fields.
The other would be to "union" the fields into a single class Hobby.
Both are semantically incorrect meaning that you can come up with cases where JSON schema validates correctly but Jackson throws an error or the information is missing in the POJO due to an omitted field.
So I think the best approach here would be to resort to Map<String, Object> instead of pojos.
So for example if a Person has a hobby the Person POJO could be:
class Person {
String name;
...
Map<String, Object> hobby;
or List<Map<String, Object> hobbies> if one can have multiple hobbies.
The approach I ended up taking is using polymorphic marshaling/unmarshaling functionality provided by Jackson.
Specifically -
Make hobby to be an interface and annotate it with #JsonTypeInfo and #JsonSubTypes
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "hobbyType",
include = JsonTypeInfo.As.EXISTING_PROPERTY,
visible = true
)
#JsonSubTypes({
#Type(value = Exercise.class, name = "exercise"),
#Type(value = Music.class, name = "music")
})
public interface Hobby {
}
Create Exercise.java and Music.java that implements this interface
#Builder
#Data
#AllArgsConstructor
public class Exercise implements Hobby {
#JsonProperty("hobbyType")
#Builder.Default
#NonNull
private final String hobbyType = "exercise";
#JsonProperty("exerciseName")
private String exerciseName;
#JsonProperty("place")
private String place;
//... (other fields)
}
Use Hobby for serialization and deserialization.
// create a Hobby object
Hobby exercise = Exercise.builder().exerciseName("swimming").place("swimmingPool").build();
// serialization
String serializedHobby = new ObjectMapper.writeValueAsString(exercise)
/**
serializedHobby looks like this ->
{
"hobbyType": "exercise",
"exerciseName": "swimming",
"place": "swimmingPool"
}
*/
// deserialization
Hobby deserializedObject = new ObjectMapper.readValue(jsonString, Hobby.class)
// deserializedObject.getClass() would return Exercise.java or Music.java based on the hobbyType
Ref: https://www.baeldung.com/jackson-inheritance

Deserializing JSON in Java using Jackson

I have the following sample fragment of JSON I am trying to deserialize.
{
"total": 2236,
"issues": [{
"id": "10142",
"key": "ID-2",
"fields": {
"attachment": [{
"id": "11132"
}]
}
}]
}
I can deserialize the data up to id and key, but cannot deserialize attachments that is in fields. My attachment class is always null
Here's my code.
Response.java
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public class Response {
#JsonProperty
private int total;
#JsonProperty
private List<Issue> issues;
}
Issue.java
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public class Issue {
#JsonProperty
private int id;
#JsonProperty
private String key;
#JsonProperty
private Fields fields;
}
Fields.java
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public class Fields {
#JsonProperty
private Attachments attachment;
}
Attachments.java
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public class Attachments {
#JsonProperty
private List<Attachment> attachment;
}
Attachments.java
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public class Attachment {
#JsonProperty
private String id;
}
In your JSON, attachment is an array, not an object.
You don't need an Attachments class, just modify Fields this way:
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public class Fields {
#JsonProperty
private List<Attachment> attachment;
}
Think of each variable in your Java classes as corresponding to an attribute in the JSON. "Attachments" is not in the JSON file. You should be able to remove it and change the variable definition in the Fields class.
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public class Fields {
#JsonProperty
private List<Attachment> attachment;
}
If you don't want to change class structure then, you can modify JSON.
In attachment, you need to add again an attachment.
JSON would be like below.
{
"total":2233,
"issues":[
{
"id":"13598",
"key":"ID-2368",
"fields":{
"attachment":{
"**attachment**":[
{
"id":"11122"
}
]
}
}
}
]
}

Struggling to Map JSON payload to model class

I am trying to map my incoming json payload to an arraylist of my model class.
I have a solution but its unintuitive.
I try to do this but get compilation errors-
ObjectMapper mapper = new ObjectMapper();
ArrayList<ModelClass> = mapper.readValue(items, RoleAttribute.class);
FYI I am trying to save this data in a Mongo collection.
Controller-
#PostMapping(value="/resource", consumes="application/json")
public Iterable<ModeClass> createResources(#RequestBody JSONObject requestBody ) throws JsonParseException, JsonMappingException, IOException {
System.out.println(requestBody.getClass());
return serviceImpl.saveResources(requestBody);
}
Model class-
#Data
#AllArgsConstructor
#Document(collection="collection-name")
public
class ModelClass{
#Field
private String ID;
#Field
private String description;
}
The payload is coming in the following format-
{
"data": [
{
"ID": "1",
"description": "desc1"
},
{
"ID": "2",
"description": "desc2"
},
{
"ID": "3",
"description": "desc3"
},
{
"ID": "4",
"description": "desc4"
}
....
]
}
I know I should be using jackson but I can't seem to figure this out. Do I need to change my POJO? Do I need to create custom Jackson config?
You can do it with json annotation. I also notice that your values are represented as data in json so that also needs to be taken care of. Look at below code. That will solve your problem.
import com.fasterxml.jackson.annotation.JsonProperty;
#Data
#AllArgsConstructor
#Document(collection="collection-name")
public class ModelClass{
#Field
#JsonProperty("ID")
private String classID;
#Field
#JsonProperty("description")
private String classDescription;
public String getClassID() {
return classID;
}
public void setClassID(String classID) {
this.classID = classID;
}
public String getClassDescription() {
return classDescription;
}
public void setClassDescription(String classDescription) {
this.classDescription = classDescription;
}
}
And wrapper Data class as below
class Data {
ModelClass[] data;
public ModelClass[] getData() {
return data;
}
public void setData(ModelClass[] data) {
this.data = data;
}
}
And json conversion code as below
ObjectMapper mapper = new ObjectMapper();
// json is your incoming json as a string. You can put inputstream also
Data values = mapper.readValue(json, Data.class);
System.out.println(values.getData().length);
System.out.println(values.getData()[0].getClassID());
You would need a container class for the data field, something like:
#Data
#Document(collection="collection-name")
public class DataClass{
private List<ModelClass> data;
}
Doing it via Jackson should be automatic this way, in controller:
public Iterable<ModeClass> createResources(#RequestBody DataClass requestBody ) {

Exception while deserializing with #JsonRootName

Desirialization does not work when there are multiple nodes available at same level as value provided in the JsonRootName
Note:
Wrapper is configured with following:
objectMapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
Class:
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonRootName(value = "response")
public class UserProfile {
String name;
String link;
}
Input JSON (which works):
{
"response": {
"name": "User one:",
"link": "Some Link"
}
}
Input JSON (which does not work)
{
"response": {
"name": "User one:",
"link": "Some Link"
},
"apiVersion" : 1.0
}
Adding a wrapper for with fields response and apiVersion wouls solve this problem which would defeat the purpose of using tag JsonRootName
How to deserialize when there is more than one field at root level (without using another wrapper class) ?

Categories