@JsonIdentityInfo is used to handle circular reference of an object by serializing the back-reference's identifier rather than serializing the complete reference.
@JsonIdentityInfo allows to serialize a POJO by id when it is encountered second time during serialization.
@JsonIdentityInfo annotation:
package com.fasterxml.jackson.annotation;
..
public @interface JsonIdentityInfo{
//Name of JSON property in which Object Id will reside
public String property() default "@id";
//Generator to use for producing Object Identifier for objects.
//Pre-defined or custom generators can be used
public Class<? extends ObjectIdGenerator<?>> generator();
//Resolver to use for producing POJO from Object Identifier.
public Class<? extends ObjectIdResolver> resolver() default SimpleObjectIdResolver.class;
//Scope is used to define applicability of an Object Id
public Class<?> scope() default Object.class;
}
ObjectIdGenerator predefined implementation
Following implementations are defined in ObjectIdGenerators class:
Example
POJOs
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Customer {
private int id;
private String name;
private Order order;
.............
}
@JsonIdentityInfo#generator=ObjectIdGenerator.class is used to produce the Identifier for the circular reference.
The attribute
@JsonIdentityInfo#property specifies the property name representing the id of the target reference. In above example 'id' refers to the Customer#id which should be used for Order#customer back-reference while serializing/deserializing.
public class Order {
private int orderId;
private List<Integer> itemIds;
private Customer customer;
.............
}
Main class
public class ExampleMain {
public static void main(String[] args) throws IOException {
Order order = new Order();
order.setOrderId(1);
order.setItemIds(List.of(10, 30));
Customer customer = new Customer();
customer.setId(2);
customer.setName("Peter");
customer.setOrder(order);
order.setCustomer(customer);
System.out.println(customer);
System.out.println("-- serializing --");
ObjectMapper om = new ObjectMapper();
String s = om.writeValueAsString(customer);
System.out.println(s);
System.out.println("-- deserializing --");
Customer customer2 = om.readValue(s, Customer.class);
System.out.println(customer2);
}
} Customer{id=2, name='Peter', order=Order{id=1, itemIds=[10, 30]}} -- serializing -- {"id":2,"name":"Peter","order":{"orderId":1,"itemIds":[10,30],"customer":2}} -- deserializing -- Customer{id=2, name='Peter', order=Order{id=1, itemIds=[10, 30]}}
Without @JsonIdentityInfo
If we don't use @JsonIdentityInfo on our Customer class then output will be:
Customer{id=2, name='Peter', order=Order{id=1, itemIds=[10, 30]}}
-- serializing --
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: com.logicbig.example.Customer["order"]->com.logicbig.example.Order["customer"]->com.logicbig.example.Customer["order"]->com.logicbig.example.Order["customer"]->com.logicbig.example.Customer["order"]->com.logicbig.example.Order["customer"]->com.logicbig.example.Customer["order"]->com.logicbig.example.Order["customer"]->com.logicbig.example.Customer["order"]->com.logicbig.example.Order["customer"]->com.logicbig.example.Customer["order"]-> .........................
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:734)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:727)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:719)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)
..................
References involving OneToMany/ManyToMany relations
@JsonIdentityInfo can also be used for the circular references involving collections. For example, following shows the use of many-to-many circular references:
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "empId")
public class Employee {
private int empId;
private List<Dept> depts;
.............
}
public class Dept {
private int deptId;
List<Employee> employees;
.............
}
public class ExampleMain2 {
public static void main(String[] args) throws IOException {
Employee emp = new Employee();
emp.setEmpId(1);
Dept dept1 = new Dept();
dept1.setDeptId(3);
dept1.setEmployees(List.of(emp));
Dept dept2 = new Dept();
dept2.setDeptId(4);
dept2.setEmployees(List.of(emp));
System.out.println("-- before serializing --");
System.out.println(emp);
System.out.println("-- json string after serializing --");
ObjectMapper om = new ObjectMapper();
String s = om.writeValueAsString(emp);
System.out.println(s);
System.out.println("-- deserializing --");
Employee employee = om.readValue(s, Employee.class);
System.out.println(employee);
System.out.println("-- deserialized back references --");
for (Dept dept : employee.getDepts()) {
System.out.println(dept + " -> " + dept.getEmployees());
}
}
} -- before serializing -- Employee{empId=1, depts=[Dept{deptId=3}, Dept{deptId=4}]} -- json string after serializing -- {"empId":1,"depts":[{"deptId":3,"employees":[1]},{"deptId":4,"employees":[1]}]} -- deserializing -- Employee{empId=1, depts=[Dept{deptId=3}, Dept{deptId=4}]} -- deserialized back references -- Dept{deptId=3} -> [Employee{empId=1, depts=[Dept{deptId=3}, Dept{deptId=4}]}] Dept{deptId=4} -> [Employee{empId=1, depts=[Dept{deptId=3}, Dept{deptId=4}]}]
Example ProjectDependencies and Technologies Used: - jackson-databind 2.9.6: General data-binding functionality for Jackson: works on core streaming API.
- JDK 10
- Maven 3.5.4
|