Use JsonInclude annotation to ignore empty values in a extended class - java

Java 1.8, Jackson library 2.1.5
I need to override the behaviour of how an object is serialized in json.
What i need is to ignore the bonus property from the serialized json response in case the value is null and the employee is a Partner employee. However trying the code below does not seem to work as expected.
class Employee{
private String bonus;
public String getBonus(){return bonus;}
public String setBonus(){this.bonus = bonus;}
}
class Partner extends Employee{
#Override
#JsonInclude(NON_NULL)
public String getBonus(){return super.getBonus();}
}
Any help?

If you can get by with excluding all null properties, then you can use the #JsonSerialize on the class. The following test runs successfully for me using Jackson 2.1.5:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.junit.Test;
public class SomeTest {
public static class Employee {
private String bonus;
public String getBonus() {
return bonus;
}
public void setBonus(String bonus) {
this.bonus = bonus;
}
}
#JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
public static class Partner extends Employee {
#Override
public String getBonus() {
return super.getBonus();
}
}
#Test
public void testSerialize() throws Exception {
Employee employee = new Employee();
Partner partner = new Partner();
ObjectMapper objectMapper = new ObjectMapper();
System.out.println("Employee: " + objectMapper.writeValueAsString(employee));
System.out.println(" Partner: " + objectMapper.writeValueAsString(partner));
}
}
Output:
Employee: {"bonus":null}
Partner: {}

Related

How to transform entities extending a generic class to another entity extending another generic class

I'm developing a service oriented platform for retrieving, creating and updating entities from DB.
The point here is that every single java entity extends AbstractEntity, so for example, I have,
MyCar extends AbstractEntity implements Serializable
This AbstractEntity has common fields (such as id or audit ones).
So I have already developed a GenericReadService that, receiving a classname and a parameterMap, can read any entity and creates a EntityActionResult<T> including a List<T extends AbstractEntity>.
My problem comes when trying to transform that T type into something like <K extends GenericDTO>, as the client asking doesn't know AbstractEntity (obvious) but only GenericDTO. Doing this for safety and modularization.
So, now, I'm stuck on transforming the ListResponse<T> to a ReturnList<K extends GenericDTO>, as I don't find the way for knowing which K class should apply for each T.
This is what I actually have written:
private GenericEntityActionResult transform (EntityActionResult result) {
AsnGenericEntityActionResult r = new AsnGenericEntityActionResult();
if (result.getCode() == EntityActionResult.Code.ENTITY || result.getCode() == EntityActionResult.Code.ENTITY_LIST ) {
r.setCode(AsnGenericEntityActionResult.Code.OK);
List <? extends GenericDTO> list = new ArrayList<>();
if (result.getEntity() != null) {
//transform this entity into DTO
//<b>SOMETHING LIKE list.add(result.getEntity());</b>
} else if (result.getList() != null && !result.getList().isEmpty()) {
for (AbstractEntity a:result.getList()) {
//transform this entity into DTO
//<b>SOMETHING LIKE list.add(result.getEntity());</b>
//list.add(result.getEntity());
}
}
r.setList(list);
}
return r;
I´m obviously stuck on the SOMETHING LIKE, but can't find a proper way of doing so.
I thought about creating a abstract <T extends GenericDTO> transformToDTO() method on AbstractEntity, but can't do it since there are lots (and i mean hundreds) of Entities extending AbstractEntity and this client-service approach is a new development for some Entities, not the whole system.
Any clever ideas?
You can try to use the Java Introspection API or some more robust library on top of this API like apache commons beanutils or even more powerful bean mapping library like Dozer or something newer see
Following example demonstrating the basic technique, only raw introspection and reflection with two compatible POJO beans.
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
class Tranformation {
public static void main(String[] args) {
MyCar car = new MyCar();
car.setId("id01");
car.setName("Komodo");
car.setMadeIn("Jakarta");
CarDTO dto = toDto(CarDTO.class, car);
System.out.println(dto);
}
public <E extends AbstractEntity, D extends GenericDTO> D toDto(Class<D> dtoClass, E entity) {
if (null == entity) {
throw new NullPointerException("Entity can not be null");
}
try {
final D ret = dtoClass.newInstance();
BeanInfo dtoBeanInfo = Introspector.getBeanInfo(dtoClass);
final Map<String, PropertyDescriptor> mapping = Arrays.stream(dtoBeanInfo.getPropertyDescriptors())
.collect(Collectors.toMap(PropertyDescriptor::getName, Function.identity()));
final BeanInfo entityBeanInfo = Introspector.getBeanInfo(entity.getClass());
Arrays.stream(entityBeanInfo.getPropertyDescriptors()).forEach(src -> {
if (!"class".equals(src.getName())) {
PropertyDescriptor dst = mapping.get(src.getName());
if (null != dst) {
try {
dst.getWriteMethod().invoke(ret, src.getReadMethod().invoke(entity, null));
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new IllegalStateException(e);
}
}
}
});
return ret;
} catch (InstantiationException | IntrospectionException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
public static class GenericDTO {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static class CarDTO extends GenericDTO {
private String madeIn;
public String getMadeIn() {
return madeIn;
}
public void setMadeIn(String madeIn) {
this.madeIn = madeIn;
}
#Override
public String toString() {
return String.format("CarDTO [id=%s, name=%s, madeIn=%s]", getId(), getName(), madeIn);
}
}
public static class AbstractEntity implements Serializable {
private static final long serialVersionUID = 70377433289079231L;
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static class MyCar extends AbstractEntity {
private static final long serialVersionUID = 8702011501211036271L;
private String madeIn;
public String getMadeIn() {
return madeIn;
}
public void setMadeIn(String madeIn) {
this.madeIn = madeIn;
}
}
}
Outputs:
CarDTO [id=id01, name=Komodo, madeIn=Jakarta]

Mixins and Jackson annotations

We currently have some mixins for our data objects in order to keep annotations out of the data objects. So for example
public class SomeDataObj {
private int a;
public int getA() { return this.a; }
public void setA(final int a) { this.a = a; }
}
public interface SomeDataObjMixin {
#JacksonXmlElementWrapper(useWrapping = false)
#JacksonXmlProperty(localName = "A")
int getA();
#JacksonXmlElementWrapper(useWrapping = false)
#JacksonXmlProperty(localName = "A")
void setA(int a);
}
Then in our object mapper class we have
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
public class OurXmlMapper extends XmlMapper {
public OurXmlMapper(final ConfigurableCaseStrategy caseStrategy) {
setPropertyNamingStrategy(caseStrategy);
setSerializationInclusion(Include.NON_NULL);
//yadda yadda
addMixin(SomeDataObj.class, SomeDataObjMixin.class);
// etc etc
}
However, for various reasons I'd like to add a new annotation to the private field in the data object, not the getter or setter. Is there a way to accomplish this through a mixin to maintain that separation? I tried creating a basic class as a mixin (not an interface) and added the private field with the
new annotation to that. This didn't accomplish what I was looking for. Any ideas?
Using Concrete class as mixin working.
// ******************* Item class *******************
public class Item {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
// ******************* ItemMixIn class *******************
#JacksonXmlRootElement(localName = "item-class")
public class ItemMixIn {
#JacksonXmlProperty(localName = "firstName")
private String name;
}
// ******************* test method *******************
public void test() throws Exception {
ObjectMapper mapper = new XmlMapper();
mapper.addMixIn(Item.class, ItemMixIn.class);
Item item = new Item();
item.setName("hemant");
String res = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(item);
System.out.println(res);
}
Output:
<item-class>
<firstName>hemant</firstName>
</item-class>
I have jackson version 2.9.5.

SpringBoot deserialization without default constructor

During the last hours I read many StackOverflow questions and articles, but none of the advices helped. What I tried:
Add #JsonCreator and #JsonProperty to both Person and Employee classes (link)
Add #JsonDeserialize(using = EmployeeDeserialize.class) to Employee class (link)
Add Lombok as dependency, set lombok.anyConstructor.addConstructorProperties=true and add #Data / #Value annotation to both Person and Employee classes (link)
Finally, I did the deserialization manually:
String json = "{\"name\": \"Unknown\",\"email\": \"please#work.now\",\"salary\":1}";
ObjectMapper objectMapper = new ObjectMapper();
Employee employee = objectMapper.readValue(json, Employee.class);
In this way I could deserialize the JSON, but as soon as I started my spring-boot-starter-web project and called
http://localhost:8080/print?name=unknown&email=please#work.now&salary=1
I got the good old BeanInstantiationException
Failed to instantiate [Employee]: No default constructor found
I run out of ideas. Does anybod know why this worked when I did the deserialization manually? And why it throws exception when I call the REST endpoint?
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
#RestController
public class EmployeeController {
#GetMapping("print")
public void print(Employee employee) {
System.out.println(employee);
}
}
public class Person {
private final String name;
#JsonCreator
public Person(#JsonProperty("name") String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class Employee extends Person {
private final String email;
private final int salary;
#JsonCreator
public Employee(
#JsonProperty("name") String name,
#JsonProperty("email") String email,
#JsonProperty("salary") int salary) {
super(name);
this.email = email;
this.salary = salary;
}
public String getEmail() {
return email;
}
public int getSalary() {
return salary;
}
}
You’re implementing JSON deserialisation, yet you’re not using any JSON.
Change to use #PostMapping on your controller method and use something like Postman or cURL to send the JSON to your /print endpoint.

Jackson ObjectMapper setSerializationInclusion() not working

I'm just getting familiar with Jackson binding. However, when I'm testing setSerializationInclusion(JsonInclude.Include.NON_NULL), I found that it's not working sometimes.
Here is my code
package com.blithe.main;
import com.blithe.model.Student;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Jackson_2_NullValue {
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
Student s = new Student();
String stundetString = mapper.writeValueAsString(s);
System.out.println(stundetString);
// exclude null fields
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
s.setName("ss");
stundetString = mapper.writeValueAsString(s);
System.out.println(stundetString);
}
}
and the POJO
package com.blithe.model;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
// #JsonIgnoreProperties(ignoreUnknown = true)
// exclude null fields for the whole class
// #JsonInclude(Include.NON_NULL)
public class Student {
// exclude the field whe it's empty ("")
// #JsonInclude(value=Include.NON_EMPTY)
private String name;
private Integer age;
private Date birth;
// Jackson ignores it
#JsonIgnore
private String nickName;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
}
the output is
{"name":null,"age":null,"birth":null}
{"name":"ss","age":null,"birth":null}
The later one should be null-value excluded, but it doesn't.
However, when I put my code this way.
package com.blithe.main;
import com.blithe.model.Student;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Jackson_2_NullValue {
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
Student s = new Student();
String stundetString = mapper.writeValueAsString(s);
System.out.println(stundetString);
// exclude null fields
// mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
s.setName("ss");
stundetString = mapper.writeValueAsString(s);
System.out.println(stundetString);
}
}
It works with the output below
{}
{"name":"ss"}
Is this normal or just some kind of bug? Do I miss anything? The only maven dependency is jackson-databind 2.7.4. Any discussion is welcomed. Thanks!
Do not change ObjectMappers settings while using it. Once mapper has been in use not all settings take effect, because of caching of serializers and deserializers.
Configure an instance once and do not change settings after first use. It is done this way for thread-safety and performance.
Update: Dead links replaced with archive.org ones
http://wiki.fasterxml.com/JacksonFAQThreadSafety
http://wiki.fasterxml.com/JacksonBestPracticesPerformance
So the point is if you are using ObjectMappers at multiple places, try not to create objects again and again. it takes the configs of first initialized.
if you keep changing on a global level it will not work.

jackson object property into primitive

I have the follow json.
{
foo:{
id:1
},
name:'Albert',
age: 32
}
How can I deserialize to Java Pojo
public class User {
private int fooId;
private String name;
private int age;
}
This is what you need to deserialize, using the JsonProperty annotations in your constructor.
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
public class User {
private int fooId;
private String name;
private int age;
public int getFooId() {
return fooId;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public User(#JsonProperty("age") Integer age, #JsonProperty("name") String name,
#JsonProperty("foo") JsonNode foo) {
this.age = age;
this.name = name;
this.fooId = foo.path("id").asInt();
}
public static void main(String[] args) {
ObjectMapper objectMapper = new ObjectMapper();
String json = "{\"foo\":{\"id\":1}, \"name\":\"Albert\", \"age\": 32}" ;
try {
User user = objectMapper.readValue(json, User.class);
System.out.print("User fooId: " + user.getFooId());
} catch (IOException e) {
e.printStackTrace();
}
}
}
Output:
User fooId: 1
Hope it helps,
Jose Luis
You can do one of the following:
Create a concrete type representing Foo:
public class Foo {
private int id;
...
}
Then in User you would have:
public class User {
private Foo foo;
...
}
Use a Map<String, Integer>:
public class User {
private Map<String, Integer> foo;
...
}
If other callers are really expecting you to have a getFooId and a setFooId, you can still provide these methods and then either delegate to Foo or the Map depending on the option you choose. Just make sure that you annotate these with #JsonIgnore since they aren't real properties.
You can use a very helpful gson google API.
First of all, create these two classes:
User class:
public class User{
Foo foo;
String name;
int age;
//getters and setters
}
Foo class:
public class Foo{
int id;
//getters and setters
}
If you have a example.json file then deserialize it as follow
Gson gson = new Gson();
User data = gson.fromJson(new BufferedReader(new FileReader(
"example.json")), new TypeToken<User>() {
}.getType());
If you have a exampleJson String then deserialize it as follow
Gson gson = new Gson();
User data = gson.fromJson(exampleJson, User.class);

Categories