I need to deserialize JSON to java class.
I have JSON like the following:
{
"data": {
"text": "John"
},
"fields":[
{
"id": "testId",
"name": "fieldName",
"options": {
"color": "#000000",
"required": true
}
},
{
"id": "testId",
"name": "fieldName1",
"options": {
"color": "#000000",
"required": false
}
}
]
}
and I need to deserialize this JSON (only "fields" section) to java class like the following:
public class Field {
public final String id;
public final String name;
public final String color;
public final boolean required;
}
and I need to get something like the following:
// The key is the id from field object (it can be the same in the multiple objects.)
Map<String, List<Field>> fields = objectMapper.readValue(json, Map<String, List<Field>>);
How can I do it using Jackson?
As long as jackson doesn't support #JsonWrapped, you have to use the following work around.
First you need to create a custom class which contains the fields:
public class Fields {
public List<Field> fields;
}
Depending on your ObjectMapper configuration you have to add #JsonIgnoreProperties(ignoreUnknown = true) to the Fields class, to ignore any other properties.
Next is that you have to define the nested Options class which is solely used temporarily:
public class Options {
public String color;
public boolean required;
}
And at last add this constructor to your Field class:
#JsonCreator
public Field(#JsonProperty("id") String id, #JsonProperty("name") String name, #JsonProperty("options") Options options){
this.id = id;
this.name = name;
this.color = options.color;
this.required = options.required;
}
The #JsonCreator annotation indicates to jackson that this constructor needs to be used for the deserialization. Also the #JsonProperty annotations are required as arguments to constructors and methods are not preserved in the bytecode
Then you can deserialize your json just like this:
List<Field> fields = objectMapper.readValue(json, Fields.class).fields;
Related
I have this JSON: (passed as Map<String, Object>)
{
"id": 1000,
"lab": [
"LAB1",
"LAB2",
"LAB3"
],
"name": "TEST",
"ref": {
"id": 1000,
"code": "REFCODE",
"description": "REF DESC"
},
"employee": {
"id": 1000,
"name": "Emp1000",
"tin": null,
"active": true
},
"contacts": [
{
"id": 1000,
"name": "Contact 1",
"emailAddress": "contact1#test.com",
"active": true,
"positions": [
{
"position": {
"id": 1000,
"code": "POS",
"description": "POS DESC"
}
}
]
}
],
"status": "NEW"
}
This is my DTO and ContactDTO:
public class DTO {
private Long id;
...
#JsonProperty("contacts")
private List<ContactDTO> contacts;
}
#Builder
public class ContactDTO implements Serializable {
private Long id;
private String name;
private String emailAddress;
private Boolean active;
#NotEmpty
private List<ContactPositionDTO> positions;
}
Here is my service class with object mapper and process method which accepts the JSON map:
private ObjectMapper objectMapper() {
var objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// objectMapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
return objectMapper;
}
public void process(final Map<String, Object> map) {
objectMapper().convertValue(map, DTO.class);
}
However, I am getting java.lang.IllegalArgumentException: Cannot deserialize value of type java.util.ArrayList
And if I add DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY I am getting a different error:
java.lang.IllegalArgumentException: Cannot construct instance of ContactDTO (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('[{id=1000, name=Contact 1, .....
You have two alternative options to fix your ContactDTO class:
Add a no-arguments constructor
public ContactDTO() { }
to the class. To fix the then upcoming compiler error
you will need to remove the #Builder annotation.
Keep the #Builder annotation
and add the #Jacksonized annotation to the class.
This will configure the generated builder to cooperate
with Jackson's deserialization.
For more details see Lombok's documentation about #Jacksonized.
To deserialize you need a No arg constructor and to use #Builder you need an all arg constructor.
So you need tu add both.
The example below should work. I just added #Getter annotation to avoid using #JsonProperty
#Getter
public static class DTO {
private Long id;
#JsonProperty("contacts")
private List<ContactDTO> contacts;
}
#Builder
#Getter
#NoArgsConstructor
#AllArgsConstructor
public static class ContactDTO implements Serializable {
private Long id;
private String name;
private String emailAddress;
private Boolean active;
private List<ContactPositionDTO> positions;
}
#Getter
public static class ContactPositionDTO {
private Position position;
#JsonProperty("effectiveStartDate")
private List<Integer> date;
#Getter
static class Position {
private Integer id;
private String code;
private String description;
}
}
NB: you can also use #Jacksonized annotation instead of #NoArgsConstructor and #AllArgsConstructor
I am using spring restTemplate to map a response to a POJO.
The response of the rest api is like this:
"attributes": {
"name": {
"type": "String",
"value": ["John Doe"]
},
"permanentResidence": {
"type": "Boolean",
"value": [true]
},
"assignments": {
"type": "Grid",
"value": [{
"id": "AIS002",
"startDate": "12012016",
"endDate": "23112016"
},{
"id": "AIS097",
"startDate": "12042017",
"endDate": "23092017"
}]
}
}
in the parent class, I have:
public class Users {
private Map<String, Attribute> attributes;
}
If all the values of were String type, then I could have done like:
public class Attribute {
private String type;
private String[] value;
}
But the values are of different types. So I thought of doing the following:
public class Attribute {
private String type;
private Object[] value;
}
The above should work, but at every step I have to find out what is the type of Object.
So, my question is can I have something like this:
public class Attribute {
#JsonProperty("type")
private String type;
#JsonProperty("value")
private String[] stringValues;
#JsonProperty("value")
private Boolean[] booleanValues;
#JsonProperty("value")
private Assignments[] assignmentValues; // for Grid type
}
But it is not working and throwing errors: Conflicting setter definitions for property "value"
What is the recommended way of handling this scenario?
I would recommend Jackson facilities for handling polymorphism here:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(value = BooleanAttribute.class, name = "Boolean"),
#JsonSubTypes.Type(value = StringAttribute.class, name = "String")
})
class Attribute {
private String type;
}
class BooleanAttribute extends Attribute {
private Boolean[] value;
}
class StringAttribute extends Attribute {
private String[] value;
}
JsonTypeInfo tells Jackson that this is a base class and the type will be determined by a JSON field named "type"
JsonSubTypes maps subtypes of Attribute to values of "type" in JSON.
If you add an appropriate subtype for Assignments and getters/setters Jackson will be able to parse your JSON.
Here is my Json response
"postedevent": [
{
"status": "true",
"event_id": "800",
"num_of_image_event": "0",
"title": "Testy",
"photo": "http://54.200.110.49/checkplanner/img/upload/21310059819profile_image_1409303464798.png",
"event_date": "2014-08-29",
"fullDate": "Friday - August 29, 2014",
"event_from": "12:00AM",
"event_to": "12:15AM",
"city": "Ahm",
"state": "CA",
"member_id": "471",
"username": "Krishna Mohan",
"pencil": "yes",
"attend": "yes",
"company": "Development"
}
]
this is java class to get java objs from json response
public class PostedEvent {
String status;
int event_id;
int num_of_image_event;
String title;
String photo;
String event_date;
String fullDate;
String event_from;
String event_to;
String city;
String state;
String member_id;
String username;
String pencil;
String attend;
String company;
}
public class PostedEvnetsList
{
ArrayList<PostedEvent> postedevent;
}
And I am parsing in this way
InputStream is = WebResponse.getResponse(url);
ObjectMapper mapper = new ObjectMapper();
PostedEvnetsList mList = null;
mList = mapper.readValue(is,PostedEvnetsList.class);
eventList = mList.postedevent;
I am getting following parse exception
jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "status" (Class com.example.jsonproforexam.PostedEvent), not marked as ignorable
I have declared same fields as in json response then why I am geting this exception
Please help
Your fields of PostedEvent and the PostedEvent field of PostedEventsList are not accessible.
You must set them as public (not recommended) or provide public getters and setters for them POJO-style.
Then Jackson will be able to de-serialize and the error will go away.
You can use the JsonProperty annotation to specify the json key
Ex:
public class PostedEvent {
#JsonProperty("status")
String status;
#JsonProperty("event_id")
String eventId;
....
....
If you have missed some fields from json in your entity class, you can use #JsonIgnoreProperties annotation to ignore the unknown fields.
#JsonIgnoreProperties(ignoreUnknown = true)
public class PostedEvent {
...
I'm trying to parse JSON like the following into object using Jackson on Android (Note: I'm not in control of the JSON format - the format comes from Yammer)
"references": [
{
"type": "user",
"id": 12345678,
"name": "Wex"
},
{
"type": "message",
"id": 12345679,
"body":
{
"plain":"A short message"
}
},
{
"type": "thread",
"id": 12345670,
"thread_starter_id": 428181699
}
]
The problem is that each entry in references is a different type of object with different properties. As a start I've got:
public static class Reference
{
public String type;
public String id;
}
I'd rather avoid putting all potential properties in one object like:
public static class Reference
{
public static class Body
{
public String plain;
}
public String type;
public String id;
public String name;
public Body body;
public String thread_starter_id;
}
And want to use separate classes that are created dependant on the type value like:
public static class ReferenceUser extends Reference
{
public String name;
}
public static class ReferenceMessage extends Reference
{
public static class Body
{
public String plain;
}
public Body body;
}
public static class ReferenceThread extends Reference
{
public String thread_starter_id;
}
So... what's the best way of getting Jackson to parse the JSON like that?
I'm currently parsing it quite simply like this:
ObjectMapper mapper = new ObjectMapper();
Reference[] references = mapper.readValue(json, Reference[].class);
you can do something like this with Jackson:
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(name = "user", value = ReferenceUser.class),
#JsonSubTypes.Type(name = "message", value = ReferenceMessage.class),
#JsonSubTypes.Type(name = "thread", value = ReferenceThread.class)
})
public class Reference {
int id;
String name;
}
This way you will generate subclasses.
John
I would appreciate any help to know the best way to deserialize the following JSON response which we receive from Salesforce into a Java object using Jackson Annotations.
"records": [
{
"attributes": {
"type": "Lead",
"url": "/services/data/v30.0/sobjects/Lead/00Qi000000Jr44XEAR"
},
"Id": "00Qi000000Jr44XEAR",
"Name": "Kristen Akin",
"Address": {
"city": null,
"country": "USA",
"state": "CA",
"stateCode": null,
"street": null
},
"Phone": "(434) 369-3100",
},
{
"attributes": {
"type": "Lead",
"url": "/services/data/v30.0/sobjects/Lead/00Qi000000Jugv2EAB"
},
"Id": "00Qi000000Jugv2EAB",
"Name": "Sarah Jones",
"Address": {
"city": null,
"country": null,
"state": "CA",
"stateCode": null,
"street": null
},
"Phone": "(408) 338-6066",
}
]}
The above JSON response is an array which contains 2 elements. I would like to represent this JSON structure as a Java Collection using Jackson, something like:
#JsonProperty("records")
ArrayList<LinkedHashMap<?, ?>> recordList
The above object representation deserializes the JSON response and represent as a Key-Value pair in HashMap but issue is representing "attributes" and "Address" subdocuments. In the above HashMap their value is being represented as the respective JSON subdocument whereas I would prefer to have Attributes subdocument gets mapped to an Attribute object and similarly Address subdocument mapped to an Address object in the HashMap, something like:
Key Value
attributes <Attributes> object
Id 00Qi000000Jr44XEAR
.....
Address <Address> object
Phone (434) 369-3100
After doing some Google search, I figured I might have to use #JsonTypeInfo and #JsonSubTypes attributes as mentioned in this link.
However, I could not figure how to use these annotations in this specific scenario. Appreciate any help on this.
If the structure of your JSON input is completely dynamic, you can read it as JsonNode or even as Map. Refer to this link for more info.
If you want to map your JSON to java classes but you don't know all the attributes in compile type, you can leverage the #JsonAnyGetter/#JsonAnySetter annotations. Here is an example based on your JSON that stores the unknown attributes for the Address class in the internal map.
public class JacksonMapping {
public static final String JSON = "...";
public static class Attributes {
public String type;
public URI url;
}
public static class Address {
public String city;
public String country;
public String state;
public Integer stateCode;
public String street;
private final Map<String, Object> otherAttributes = new HashMap<>();
#JsonAnySetter
public void setProperty(String name, Object value) {
otherAttributes.put(name, value);
}
#JsonAnyGetter
public Map<String, Object> getOtherAttributes() {
return otherAttributes;
}
}
public static class Record {
#JsonProperty("Id")
public String id;
#JsonProperty("Name")
public String name;
public Attributes attributes;
#JsonProperty("Address")
public Address address;
#JsonProperty("Phone")
public String phone;
}
public static class RecordList {
public List<Record> records;
}
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
RecordList recordList = mapper.readValue(JSON, RecordList.class);
System.out.println(mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(recordList));
}
}
I can also try to generate java objects from your JSON with a help from a tool. For example this one: http://www.jsonschema2pojo.org