I am having a question with Jackson that I think should be simple to solve, but it is killing me.
Let's say I have a java POJO class that looks like this (assume Getters and Setters for me):
class User {
private String name;
private Integer age;
}
And I want to deserialize JSON that looks like this into a User object:
{
"user":
{
"name":"Sam Smith",
"age":1
}
}
Jackson is giving me issues because the User is not the first-level object in the JSON. I could obviously make a UserWrapper class that has a single User object and then deserialize using that but I know there must be a more elegant solution.
How should I do this?
edit: this solution only works for jackson < 2.0
For your case there is a simple solution:
You need to annotate your model class with #JsonRootName(value = "user");
You need to configure your mapper with om.configure(Feature.UNWRAP_ROOT_VALUE, true); (as for 1.9) and om.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true); (for version 2).
That's it!
#JsonRootName(value = "user")
public static class User {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(final Integer age) {
this.age = age;
}
#Override
public String toString() {
return "User [name=" + name + ", age=" + age + "]";
}
}
ObjectMapper om = new ObjectMapper();
om.configure(Feature.UNWRAP_ROOT_VALUE, true);
System.out.println(om.readValue("{ \"user\": { \"name\":\"Sam Smith\", \"age\":1 }}", User.class));
this will print:
User [name=Sam Smith, age=1]
Related
I am integrating with a third party API that responses with an object like this:
{
"name": {
"firstName": "John",
"lastName": "Doe"
}
}
The corresponding Java class looks like this:
class Name {
private String firstName;
private String lastName;
}
Problem: When the name is supposed to be null the API returns an empty string instead of null or an empty object:
{
"name": ""
}
Questing: How to tolerate this behavior on the client side keeping in mind that I call the API using Spring's RestTemplate and Jackson?
In other words, how can I tell Jackson to treat an empty string passed into the name attribute as null, or as an object of the class Name with null properties?
Note: It is not possible to make the API work correctly. I have to handle it on my side.
You either use try / catch and handle the error. Or you write your own Deserializer if you have to handle different scenarios.
Try Catch
Name name;
try {
// Your restTemplate code, will throw Exception when cannot serialize
} catch (Execption e) {
name = null;
}
Deserializer docs:
https://fasterxml.github.io/jackson-databind/javadoc/2.3.0/com/fasterxml/jackson/databind/JsonDeserializer.html
Example on how you can deserialize (not tested)
// On your class specify Deserialzer
#JsonDeserialize(using = YourNameClassDeserializer.class)
public class YourNameClass {
// your class here
}
// Your Deserializer class
public class YourNameClassDeserializer extends StdDeserializer<Item> {
#Override
public Item deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
// Use JsonNode to figure out what you are getting and create the object by yourself
return new YourNameClass(firstName, lastName);
}
}
You can one of these solution, both will work for your case.
Jackson has a deserialization feature for this called ACCEPT_EMPTY_STRINGS_AS_NULL_OBJECTS. With Spring boot, you can enable this by enabling the following property within application.properties:
spring.jackson.deserialization.accept-empty-string-as-null-object=true
After that, Jackson will deserialize empty strings as null.
Be aware, since this is a global deserialization feature, this will also happen when the first name would be an empty string.
Try this. This should work. I would say you need to change your java model a bit.
#JsonDeserialize(using = NameDeserializer.class)
public class NameWrapper {
private Name name;
public NameWrapper() {
}
public NameWrapper(Name name) {
this.name = name;
}
public Name getName() {
return name;
}
public void setName(Name name) {
this.name = name;
}
}
Name class will be like
public class Name {
private String firstName;
private String lastName;
public Name() {
}
public Name(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = 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;
}
}
And lastly the deserializer
public class NameDeserializer extends JsonDeserializer {
#Override
public NameWrapper deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException
{
ObjectCodec oc = jsonParser.getCodec();
JsonNode node = oc.readTree(jsonParser);
if(node.get("name").get("firstName") == null && node.get("name").textValue().isEmpty())
return null;
else {
String firstName = node.get("name").get("firstName").textValue();
String lastName = node.get("name").get("lastName").textValue();
return new NameWrapper(new Name(firstName, lastName));
}
}
}
This would work with both these json's
{
"name": {
"firstName": "John",
"lastName": "Doe"
}
}
and
{
"name": ""
}
I am using Jersy for producing Json in an application.
The code snippet which produces Json is as follows
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("sample")
public List<test> displaySampleMessage(#PathParam("id") int id)
{
System.out.println(id);
List<test> sample1 = new ArrayList<>();
test temp1 = new test();
temp1.setName("abc");
sample1.add(temp1);
return sample1;
}
Test is simple java class with the following code
package webServiceTester;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class test
{
private String name;
private int age;
public test()
{
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public test(String name, int age) {
super();
this.name = name;
this.age = age;
}
}
Then when I run this web service I get the following output
I do not t want to get age = 0 here because I have not set the age property of my object.
What is its solution. I want age to be appeared if I set the value otherwise it should not appear..
Use Jakson, here you can find a
JSON example with Jersey + Jackson.
So you can use #JsonSerialize annotation to exclude null or empty fields.
#JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
or
#JsonSerialize(include=JsonSerialize.Inclusion.NON_EMPTY)
However it's not a good practice to exclude null or empty fields in restful applications because it may lead errors in client side.
What are the #JsonTypeInfo and #JsonSubTypes annotations used for in Jackson?
public class Lion extends Animal {
private String name;
#JsonCreator
public Lion(#JsonProperty("name") String name) {
this.name = name;
}
public String getName() {
return name;
}
public String getSound() {
return "Roar";
}
public String getType() {
return "carnivorous";
}
public boolean isEndangered() {
return true;
}
#Override
public String toString() {
return "Lion [name=" + name + ", getName()=" + getName() + ", getSound()=" + getSound() + ", getType()=" + getType() + ", isEndangered()="
+ isEndangered() + "]";
}
}
========================================
public class Elephant extends Animal {
#JsonProperty
private String name;
#JsonCreator
public Elephant(#JsonProperty("name") String name) {
this.name = name;
}
public String getName() {
return name;
}
public String getSound() {
return "trumpet";
}
public String getType() {
return "herbivorous";
}
public boolean isEndangered() {
return false;
}
#Override
public String toString() {
return "Elephant [name=" + name + ", getName()=" + getName() + ", getSound()=" + getSound() + ", getType()=" + getType()
+ ", isEndangered()=" + isEndangered() + "]";
}
}
==============================================
#JsonTypeInfo (use = JsonTypeInfo.Id.CLASS, include = As.PROPERTY, property = "classNameExtenral")
#JsonSubTypes ({#Type (value = Lion.class, name = "lion"), #Type (value = Elephant.class, name = "elephant")})
public abstract class Animal {
#JsonProperty ("name")
String name;
#JsonProperty ("sound")
String sound;
#JsonProperty ("type")
String type;
#JsonProperty ("endangered")
boolean endangered;
}
public static void main(String[] args){
Lion lion = new Lion("Simba");
Elephant elephant = new Elephant("Manny");
List<Animal> animals = new ArrayList<>();
animals.add(lion);
animals.add(elephant);
}
What I understand is that it additionally preserves the concrete type of object being serialised along with the actual data.
What is not clear to me is what is the actual advantage/gain during deserialization.
Not getting any significant documentation apart from the java docs. Can anyone please help out here or provide some docs around the same.
The purpose of these annotations is to support polymorphism on deserialization. When deserializing the actual code being executed will know the class of what it expects. E.g., the type of some field being deserialized into. But if that class has subclasses (i.e., subtypes) how does the generic Jackson deserializer know which actual class the string being deserialized is? It's got to create an instance of some concrete type (the class or one of its subclasses) and fill it up. The only way it can know which one to create is if that information is written into the serialization in the first place.
As this answer says there are three ways to do it - you pick the one that's appropriate for your use case. #JsonTypeInfo + #JsonSubtypes is one of those ways - it works great when you know, at compile time, all of the possible subtypes that could exist for the class in question.
I have built (Using Builder Pattern) an Employee object with three fields Name, Age and Gender.
public class Employee {
private String name;
private String age;
private String gender;
// Constructor
private Employee(Builder builder) {
this.name = builder.name;
this.age = builder.age;
this.gender = builder.gender;
}
// Employee Builder
public static class Builder {
private String name;
private String age;
private String gender;
public Builder name(String name) {
this.name = name;
return this;
}
public Builder age(String age) {
this.age = age;
return this;
}
public Builder gender(String gender) {
this.gender = gender;
return this;
}
}
// Getters
public String getName() {
return name;
}
public String getAge() {
return age;
}
public String getGender() {
return gender;
}
}
Now in the Following Class I have built my Employee Object,
public class TestEmployee {
public static void main(String[] args) throws IOException {
Employee employee = new Employee.Builder().age("23").gender("Male").name("John").build();
System.out.println("Name : " + employee.getName());
System.out.println("Age : " + employee.getAge());
System.out.println("Gender : " + employee.getGender());
}
}
How can I modify the Age of the Employee "John" by breaking the already built employee object?
FYI : I don't want to have Setters in my Employee object.
You want to modify an immutable object. Do you see the problem there?
Either add setters (or any methods that mutate the state) or accept that the object is immutable.
You can of course create a new object based on the values of the old one, but it won't be the same object then.
Build another one using Copy-On-Write (reuse existing fields but change age).
Employee.Builder()
.age(employee.getAge() + 1)
.gender(employee.getGender())
.name(employee.getName())
.build();
Keep in mind, it will be another object.
ahh I was also trying to solve my problem with this approach and ironically I stumbled upon this question. I understand the challenge here, we are trying to modify an object with same builder setters but doing so we'll end up with an new object.
I figured out that there is a solution, so after approx 5yrs here's my answer LOL(to someone who's gonna end up here)
first, instead of creating duplicate properties of OuterClass in inner static Builder class. *declare an instance of outer class in Builder class. which means your build() method will return that instance.
this *declaration of instance is intended, as I am planning to create it internally or gonna ask for its memory from outside.
public class Employee {
private String name;
private String age;
private String gender;
public Static Builder builder()
{
return new Builder();
}
public Static Builder modifier(Employee employee)
{
return new Builder(employee);
}
// Employee Builder
public static class Builder {
private Employee employee;
public Builder(Employee employee)
{
this.employee = employee;
}
public Builder()
{
this.employee = new Employee();
}
public Builder name(String name) {
this.employee.name = name;
return this;
}
public Builder age(String age) {
this.employee.age = age;
return this;
}
public Builder gender(String gender) {
this.gender = gender;
return this;
}
public Employee build()
{
return this.employee;
}
}
// Getters
public String getName() {
return name;
}
public String getAge() {
return age;
}
public String getGender() {
return gender;
}
}
notice the tweak here, I introduced a modifier that takes Employee object and further allows client to modify it with Builder pattern.
So your client will use it like this...
to create new Employee
Employee employee = Employee.builder().name("abc).age(20).build();
to modify same instance
Employee.modifier(employee)
.name("xyz")
.build();
If you don't want to put setters (and make Employee mutable) you can't modify age of john... instead of this, what you can do is:
employee = new Employee.Builder()
.age("21")
.gender(employee.getGender())
.name(employee.getName())
.build();
If you don't want setters or public non-final fields, then you can add an extra constructor to the builder which will cause an initial state matching the instance, but with the builder setters available. This won't modify the original object, but create a new one based on it.
public static class Builder {
private String name;
private String age;
private String gender;
public Builder(Employee employee) {
this.name = employee.getName();
this.age = employee.getAge();
this.gender = employee.getGender();
}
public Builder name(String name) {
this.name = name;
return this;
}
public Builder age(String age) {
this.age = age;
return this;
}
public Builder gender(String gender) {
this.gender = gender;
return this;
}
public Employee build() {
return new Employee(this);
}
}
You can then use it as following.
Employee employee = new Employee.Builder().age("23").gender("Male").name("John").build();
Employee employee2 = new Employee.Builder(employee).name("Jane").build();
I am writing a Restful webservice which would receive data in the below format.
{
"myOrder": {
"submitDate": "2015-04-16T02:52:01.406-04:00",
"supplier": "Amazon",
"orderName": "Wifi Router",
"submittedBy": "Gaurav Varma",
"price": {
"value": "2000",
"currency": "USD"
},
"address": {
"name": "My home",
"address": "Unknow island",
"city": "Mainland China",
"state": "Xinjiang",
"contact": {
"firstName": "Gaurav",
"lastName": "Varma",
"phone": "000-000-0000",
"email": "test#gv.com"
}
}
}
}
To read that data I am considering Jackson or GSON frameworks. The easiest way would be to use a Java POJO which has exactly the same structure as the json request. But for me the structure of Java POJOs is different. I have four different pojo as mentioned below :
Submitter.java
- SubmittedBy
- SubmitDate
Order.java
- Supplier
- OrderName
Price.java
- Value
- Currency
Address.java
- Name
- Address
- City
- State
Contact.java
- FirstName
- LastName
- Phone
- Email
Question : Is it a way to parse the json once into five different POJOs. May be some annotation based approach where we can map json attribute to respective pojo attribute? Any framework available for it?
Thanks in advance !
I'm currently using Jackson on my project. You have the option of annotating your POJO fields with #JsonProperty or #JsonUnwrapped. You would use #JsonUnwrapped on Order, for example, and then Order would have two fields (supplier and orderName) that use #JsonProperty.
See here for more details.
You could use eclipse link moxy for this. It uses JAXB style annotations for field to JSON/XML mapping.
Moxy is part of eclipse link.
Wikipedia:
EclipseLink is the open source Eclipse Persistence Services Project
from the Eclipse Foundation. The software provides an extensible
framework that allows Java developers to interact with various data
services, including databases, web services, Object XML mapping (OXM),
and Enterprise Information Systems (EIS).
So in your code you would use it like;
Model A:
#XmlElement(name="completed_in")
public float getCompletedIn() {
return completedIn;
}
Model B:
#XmlElement(name="created_at")
#XmlJavaTypeAdapter(DateAdapter.class)
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
#XmlElement(name="from_user")
public String getFromUser() {
return fromUser;
}
Json:
{
"completed_in":0.153,
{
"created_at":"Fri, 12 Aug 2011 01:14:57 +0000",
"from_user":"stackfeed",
you can use the composition design pattern and have an instance of each object in a wrapper class. Or you can try to parse the json into a map and write code to instantiate and set the variables as needed.
You could use Jackson; I think you need a POJO to wrapp the Order and Address like
class FullOrder {
Order order;
Address address;
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
With this you can easily use Jackson
String json; // your json here
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.readValue(json, FullOrder.class);
And that will parse the json into your pojo. Hope it helps you
The full structure
class Submitter {
private Date submittedBy;
private Date submitDate;
public Date getSubmittedBy() {
return SubmittedBy;
}
public void setSubmittedBy(Date submittedBy) {
SubmittedBy = submittedBy;
}
public Date getSubmitDate() {
return SubmitDate;
}
public void setSubmitDate(Date submitDate) {
SubmitDate = submitDate;
}
}
class Order {
private String supplier;
private String orderName;
private Price price;
private Submitter submitter;
public Price getPrice() {
return price;
}
public void setPrice(Price price) {
this.price = price;
}
public Submitter getSubmitter() {
return submitter;
}
public void setSubmitter(Submitter submitter) {
this.submitter = submitter;
}
public String getSupplier() {
return Supplier;
}
public void setSupplier(String supplier) {
Supplier = supplier;
}
public String getOrderName() {
return OrderName;
}
public void setOrderName(String orderName) {
OrderName = orderName;
}
}
class Price {
private int value;
private int currency;
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public int getCurrency() {
return currency;
}
public void setCurrency(int currency) {
this.currency = currency;
}
}
class Address {
private String name;
private String address;
private String city;
private String state;
private Contact contact;
public Contact getContact() {
return contact;
}
public void setContact(Contact contact) {
this.contact = contact;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
class Contact {
String firstName;
String lastName;
long phone;
String email;
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;
}
public long getPhone() {
return phone;
}
public void setPhone(long phone) {
this.phone = phone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
class FullOrder {
Order myOrder;
Address address;
public Order getMyOrder() {
return order;
}
public void setMyOrder(Order order) {
this.order = order;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
This is structure of your json, you only need to copy it and use the Object mapper to parse the json to the pojo (FullOrder) that contains the other pojos and properties
String json; // your json here
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.readValue(json, FullOrder.class);
I figured out the solution approach. Posting for other users. The complete implementation is on my blog - http://javareferencegv.blogspot.com/2015/04/parse-json-into-multiple-java-pojos.html
So basically 3 points regarding solution approach:
We use Jackson annotation - #JsonIgnoreProperties. This would make
sure only those fields in Pojo are mapped to JSON attributes. So we
read the json twice, once mapping to Order.java and then to
Submitter.java. Both gets the correspondingly mapped fields.
We use Jackson annotation - #JsonProperty. This lets us map the exact JSON attribute to a field in POJO. The annotation makes sure different named attributes in JSON and POJO are mapped.
Jackson doesn't provide any annotation to perform #JsonWrapped (The vice-versa #JsonUnwrapped is available for serialization). Hence, we map Price as an attribute in Order.java.
The main class looks like this :
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonDeserializer {
public static void main(String[] args) {
try {
// ObjectMapper provides functionality for data binding between
ObjectMapper mapper = new ObjectMapper();
String jsonString = "{\"submitDate\":\"2015-04-16\",\"submittedBy\":\"Gaurav Varma\",\"supplier\":\"Amazon\",\"orderName\":\"This is my order\","
+ "\"price\": {\"value\": \"2000\",\"currency\": \"USD\"}"
+ "}";
System.out.println("JSON String: " + jsonString);
// Deserialize JSON to java format and write to specific POJOs
Submitter submitterObj = mapper.readValue(jsonString, Submitter.class);
Order orderObj = mapper.readValue(jsonString, Order.class);
Price priceObj = orderObj.getPrice();
System.out.println("submitterObj: " + submitterObj);
System.out.println("orderObj: " + orderObj);
System.out.println("priceObj: " + priceObj);
} catch (Exception e) {
e.printStackTrace();
}
}
}