The problem seems to be the reations between Objects, in my example i'll use Customer and Order , a customer has a list of orders, and the order contains a reference back to the Customer.
public class Customer {
private Long id;
private String name;
private List<Order> orders;
.....
public class Order {
private Long id;
private Customer customer;
this is a common situation :)
String orderAsString = objectMapper.writeValueAsString(customer);
this line will generate a StackOverflowException
com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: ...)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:706)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)
.....
For this problem I like two solutions, the other ones needs extra work and will lose the ability to transform the JSON back to java Objects.
@JsonManagedReference
and @JsonBackReference
this one works wery well, it will exclude back references from output but will keep the relation between them, when we will recreate the objects we will have the entore structure and all references in place
public class Customer {
private Long id;
private String name;
@JsonManagedReference
private List<Order> orders;
public class Order {
private Long id;
@JsonBackReference
private Customer customer;
the output :
{
"id" : 1,
"name" : "The Cusotmer",
"orders" : [ {
"id" : 1
}, {
"id" : 2
} ]
}
as you can see the customer is not included in orders.
the code :
String orderAsString = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(customer);
System.out.println(orderAsString);
Customer customerFromJson = objectMapper.readValue(orderAsString, Customer.class);
when we are transforming the JSON to Java objects we will have customers in place:
JsonIdentityInfo
Whist this one you have to take care about scope and also you need to have an id on your POJO otherwise will be hard to implement it correct.
@JsonIdentityInfo( scope = Customer.class,
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id"
)
public class Customer {
private Long id;
.....
@JsonIdentityInfo(
scope = Order.class,
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id"
)
public class Order {
private Long id;
the code used for this one is the same
String orderAsString = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(customer);
System.out.println(orderAsString);
Customer customerFromJson = objectMapper.readValue(orderAsString, Customer.class);
and the JSON is like on solution 1, also the the Objects created from JSON .
Note , not using scope will throw :
com.fasterxml.jackson.databind.JsonMappingException: Already had POJO for id (java.lang.Long) [[ObjectId: key=1, type=com.fasterxml.jackson.databind.deser.impl.PropertyBasedObjectIdGenerator, scope=java.lang.Object]]
Other examples , use them only if you are forced to do this
StdSerializer
final ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(Order.class, new StdSerializer<Order>(Order.class) {
@Override
public void serialize(Order value, JsonGenerator jsonGenerator, SerializerProvider provider) throws IOException {
jsonGenerator.writeStartObject();
jsonGenerator.writeNumberField("id", value.getId());
jsonGenerator.writeEndObject();
}
});
objectMapper.registerModule(module);
try {
String orderAsString = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(customer);
System.out.println(orderAsString);
Customer customerFromJson = objectMapper.readValue(orderAsString, Customer.class);
System.out.println(customerFromJson);
you will have dirty hands after writing this kind of solution :), and the orders will contains a null customer !
forced 2 : FilterProvider
@JsonFilter("filterOrder")
public class Order {
private Long id;
private Customer customer;
...
and the code :
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
String[] ordrerIgnorableFieldNames = { "customer" };
FilterProvider filters = new SimpleFilterProvider()
.addFilter("filterOrder", SimpleBeanPropertyFilter.serializeAllExcept(ordrerIgnorableFieldNames));
ObjectWriter writer = objectMapper.writer(filters);
try {
String orderAsString = writer.writeValueAsString(customer);
System.out.println(orderAsString);
Customer customerFromJson = objectMapper.readValue(orderAsString, Customer.class);
System.out.println(customerFromJson);
again, the orders will contains null customer.
download sources from GitHub Sources