JavaScript Required

We're sorry, but we doesn't work properly without JavaScript enabled.

Spring Custom Serializers with @JsonIdentityInfo

Technology: Jackson-databind is the one of the libraries widely used in modern spring-based applications for serialization or deserialization from/to JSON. Jackson library will convert object to JSON, and vice versa.

If the object is complex, and need to customize the serialization/ deserialization then we need to write Custom Serializer which extends default Jackson provided serializer i.e. com.fasterxml.jackson.databind.ser.std.StdSerializer.

Defining Custom Serializer: we will create StudentCustomSerializer class to customize the Student class, it must extend
spring-custom-serializers-with-@jsonidentityinfo

com.fasterxml.jackson.databind.ser.std.StdSerializer and create required constructor methods and default constructor with no-arguments.

We need to implement one abstract serialize method, how want to serialize the object.

public class StudentSerializer extends StdSerializer<Student> { private static final long serialVersionUID = 1L; public StudentSerializer() { this(null); } protected StudentSerializer(Class<Student> t) { super(t); } @Override public void serialize(Student student, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeStartObject(); provider.defaultSerializeField("id", student.getId(), gen); provider.defaultSerializeField("name_custom", student.getName(), gen); provider.defaultSerializeField("name_custom", student.getEmail(), gen); gen.writeEndObject(); } }

And Student class look like below:

@JsonSerialize(using = StudentSerializer.class) public class Student { private Long id; private String name; private String email; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } }

Here we are annotating the Student class with @JsonSerialize annotation and passing above custom Serializer class to using attribute, so that during the serializing it will be invoked.

If we run and our main class will look like below:

public class Main { public static void main(String[] args) throws Exception { final Student student = new Student(); student.setId(1L); student.setEmail("student@gmail.com"); student.setName("s1"); final ObjectMapper mapper = new ObjectMapper(); final String json = mapper.writeValueAsString(student); System.out.println(json); } }

If we run the program output will look like below:

{"id":1,"name_custom":"s1","name_custom":"student@gmail.com"}

Circular References:

Create course POJO type class and add the List in student class.

Create student variable in Courses class, so that it will maintain one-to-many relation.

Student.java

public class Student { private Long id; private String name; private String email; private List<Course> courses = new ArrayList<>(); // getters and setters } public class Course { private Long id; private String name; private Student student; //getters and setters }

If we serialize the student class using objectmapper, as student is depends on Course, Course will depend on Student Serailization, we can observe a cycle, java will throw stackOverFlowException, to overcome this circular Reference, Jackson library provides new annotation @JsonIdentityInfo.

Add the below line at class level of the student so that from 2nd time onwards it will serialize the only id property instead of whole object.

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")

After adding student class look like below:

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") public class Student { private Long id; private String name; private String email; private List<Course> courses = new ArrayList<>(); //getters and setters }

main class :

public static void main(String[] args) throws Exception { final Student student = new Student(); student.setId(1L); student.setEmail("student@gmail.com"); student.setName("s1"); final Course course1 = new Course(); course1.setId(1L); course1.setName("course1"); course1.setStudent(student); final Course course2 = new Course(); course2.setId(2L); course2.setName("course2"); course2.setStudent(student); final List<Course> courses = new ArrayList<>(); courses.add(course1); courses.add(course2); student.setCourses(courses); final ObjectMapper mapper = new ObjectMapper(); final String json = mapper.writeValueAsString(student); System.out.println(json); } }

If we run without @JsonIdentityInfo annotation, we can observer below error in console.

jsonidentityinfo-annotation

If we run with annotation, we can see the serialized student output:

{"id":1,"name":"s1","email":"student@gmail.com","courses":[{"id":1,"name":"course1","student":1},{"id":2,"name":"course2","student":1}]}

Now we can observer course class will only contain student id, other fields are skipped.

Custom Serializer AutoWired Problem:

If we would like @autowired some of the bean into our custom serializer, by default not possible, as custom serializer is not of spring bean type.

objectMapper provide one to way inject using objectInstantiator, spring extends this objectInstantiator to autowire the beans inside custom serializer class using below code.

ObjectMapper mapper = Jackson2ObjectMapperBuilder.json().build();

mapper.setHandlerInstantiator(new SpringHandlerInstantiator(this.applicationContext.getAutowireCapableBeanFactory()));

This we can inject spring beans into custom serializer, and we can override the serialize according to our needs.

Conclusion: Jackson is one of the libraries to used serialize the object from/to JSON, it provides useful annotation to avoid Circular reference.

Jackson library also provides the way to customize the serialization, and also we saw how to inject spring beans into custom serializer using HandlerInstantiator.

Read More:

Ast Note

Some of our clients

team