Jackson ObjectMapper setSerializationInclusion() not working - java

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.

Related

Junit test coverage is showing 0%

I am writing test case for mapper method where new object instance is creating for mapping. How to mock all mapper objects and set in setter? Eclipse tool passing test but jacoco is giving 0% coverage on this code.
public class NewUseInsideMethods {
public StudentOldTestingReturn OldLogicTesting(StudentOldTesting Student) {
StudentOldTestingReturn sturentReturn = new StudentOldTestingReturn();
OldBatchName batchName = new OldBatchName();
batchName.setId("1");
batchName.setName("test");
sturentReturn.setId(batchName.getId());
sturentReturn.setName(Student.getName());
return sturentReturn;
}
}
public class OldBatchName {
private String name;
private String id;
//setter and getter
}
public class StudentOldTesting {
private String name;
private String id;
//setter and getter
}
public class StudentOldTestingReturn {
private String name;
private String id;
//setter and getter
}
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
#ExtendWith(MockitoExtension.class)
class NewUseInsideMethodsTest {
#Test
void OldLogicTesting() throws Exception {
StudentOldTesting oldTesting = new StudentOldTesting();
oldTesting.setName("shraban");
oldTesting.setId("1");
NewUseInsideMethods insideMethods = new NewUseInsideMethods();
StudentOldTestingReturn result = insideMethods.OldLogicTesting(oldTesting);
Assertions.assertNotNull(result);
Assertions.assertEquals("shraban", result.getName());
Assertions.assertEquals("1", result.getId());
}
}

Is it possible to use IntelliJ's 'Analyze Data Flow to Here' feature with Java Lombok?

I have recently done an experiment to see how we can use Lombok to reduce boilerplate in our code.
The issue:
When creating a simple data class with a builder through Lombok annotations, in IntelliJ IDEA, I cannot right click a field, then select Analyze Data Flow to Here.
This is using the latest IntelliJ Lombok Plugin. IntelliJ Ultimate 2019.2.3.
Is there any fix for this or is it simply not supported?
Example 1 - no lombok:
public class Person {
private String name;
private int age;
private Person() {
}
public Person(Builder builder) {
name = builder.name;
age = builder.age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public static class Builder {
private String name;
private int age;
public Builder name(String val) {
this.name = val;
return this;
}
public Builder age(int val) {
this.age = val;
return this;
}
public Person build() {
return new Person(this);
}
}
}
public class Main {
public static void main(String[] args) {
Person person = new Person.Builder().name("tom").age(3).build();
}
}
With the above code, when I right click the "name" variable and select analyse dataflow to here, I am able to see the dataflow. As shown in screenshot:
Example 2 - with Lombok:
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
#NoArgsConstructor
#Builder
#Getter
public class Person {
private String name;
private int age;
}
public class Main {
public static void main(String[] args) {
Person person = Person.builder().name("tom").age(3).build();
}
}
With the above code example, selecting 'analyse data flow to here' on the name field will show the variable name, but with no tree to expand as shown in the screenshot.
"Analyze data flow to here" will not work with generated code provided by Lombok annotations.

jackson AfterburnerModule gives a warning in the log

After enabling AfterburnerModule I am seeing the below warning message in the logs. Without AfterburnerModule everything works fine.
Sep 06, 2017 9:11:39 AM com.fasterxml.jackson.module.afterburner.deser.BeanPropertyMutator _reportProblem
WARNING: Disabling Afterburner deserialization for class com.test.Child (field #0; mutator com.test.Child$Access4JacksonDeserializerdc6e0fab), due to access error (type java.lang.IllegalAccessError, message=tried to access method com.test.test2.Parent.setSurname(Ljava/lang/String;)V from class com.test.Child$Access4JacksonDeserializerdc6e0fab)
java.lang.IllegalAccessError: tried to access method com.test.test2.Parent.setSurname(Ljava/lang/String;)V from class com.test.Child$Access4JacksonDeserializerdc6e0fab
at com.test.Child$Access4JacksonDeserializerdc6e0fab.stringSetter(com/test/Child$Access4JacksonDeserializer.java)
at com.fasterxml.jackson.module.afterburner.deser.BeanPropertyMutator.stringSetter(BeanPropertyMutator.java:123)
at com.fasterxml.jackson.module.afterburner.deser.SettableStringMethodProperty.deserializeAndSet(SettableStringMethodProperty.java:60)
at com.fasterxml.jackson.module.afterburner.deser.SuperSonicBeanDeserializer.deserialize(SuperSonicBeanDeserializer.java:156)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3807)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2797)
at com.test.JacksonStuff.main(JacksonStuff.java:19)
My class are as follows:
package com.test.test2;
import com.fasterxml.jackson.annotation.JsonProperty;
public class Parent {
#JsonProperty("surname")
private String surname;
protected Parent() {
}
public Parent(String surname) {
this.surname = surname;
}
public String getSurname() {
return surname;
}
protected void setSurname(String surname) {
this.surname = surname;
}}
package com.test;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.test.test2.Parent;
public class Child extends Parent {
#JsonProperty("name")
private String test;
public Child() {
super();
}
public Child(String var1) {
super(var1);
}
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}}
package com.test;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.module.afterburner.AfterburnerModule;
import java.io.IOException;
public class JacksonStuff {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
AfterburnerModule module = new AfterburnerModule();
mapper.registerModule(module);
String json = "{\"surname\":\"abc\"}";
Child value = mapper.readValue(json, Child.class);
}}
How do I avoid getting this warning in the logs? I cannot modify parent.java as it is from a third party lib and not under my control.
Because void setSurname is protected. Set it to public.

Use JsonInclude annotation to ignore empty values in a extended class

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: {}

Simple Java to XML example

I've read a time ago about generate xml from Java using annotations, but I'm not finding a simple example now.
If I want to make a xml file like:
<x:element uid="asdf">value</x:element>
from my java class:
public class Element {
private String uid = "asdf";
private String value = "value";
}
Which annotations should I use to perform that? (I have a xml-schema, if this helps the generation)
--update
The javax.xml.bind.annotation package have the annotations, "but I still haven't found what I'm looking for": an exemple of usage.. :)
Found it:
import java.io.FileOutputStream;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlRootElement;
public class JavaToXMLDemo {
public static void main(String[] args) throws Exception {
JAXBContext context = JAXBContext.newInstance(Employee.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
Employee object = new Employee();
object.setCode("CA");
object.setName("Cath");
object.setSalary(300);
m.marshal(object, System.out);
}
}
#XmlRootElement
class Employee {
private String code;
private String name;
private int salary;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSalary() {
return salary;
}
public void setSalary(int population) {
this.salary = population;
}
}
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<employee>
<code>CA</code>
<name>Cath</name>
<salary>300</salary>
</employee>
From: http://www.java2s.com/Code/JavaAPI/javax.xml.bind.annotation/javaxxmlbindannotationXmlRootElement.htm
For the benefit of anyone else hitting this thread, I imagine you did the following:
#XmlRootElement
public class Element {
#XmlAttribute
private String uid = "asdf";
#XmlValue
private String value = "value";
}
For More Information
http://bdoughan.blogspot.com/2011/06/jaxb-and-complex-types-with-simple.html
There are various tools that you can use to do this. XStream (http://x-stream.github.io/) is a reasonably easy tool to use that allows you to use annotations to determine the schema of XML that is created.

Categories