How to Solve Spring Boot Bidirectional JSON Recursion with 6 Jackson Techniques

This article explains why bidirectional entity relationships cause infinite JSON recursion in Spring Boot, demonstrates the problem with sample code and console output, and presents six practical Jackson solutions—including @JsonManagedReference, @JsonIdentityInfo, @JsonIgnore, @JsonView, and custom serializers—complete with code snippets and result screenshots.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
How to Solve Spring Boot Bidirectional JSON Recursion with 6 Jackson Techniques

Introduction

In Spring Boot development, handling bidirectional relationships such as a one‑to‑many association between Customer and Order often leads to infinite recursion when Jackson serializes the objects to JSON, resulting in a "Recursion depth exceeded" error.

Problem Demonstration

Entity definitions:

public class Customer {
    private Long id;
    private String name;
    private List<Order> orders = new ArrayList<>();
}

public class Order {
    private Long id;
    private String sno;
    private BigDecimal total;
    private Customer customer;
}

Controller returning a Customer with populated orders:

@GetMapping("")
public ResponseEntity<?> query() {
    Customer customer = new Customer();
    customer.setId(1L);
    customer.setName("Pack");
    customer.setOrders(List.of(
        new Order("S-001", BigDecimal.valueOf(100), customer),
        new Order("S-002", BigDecimal.valueOf(200), customer),
        new Order("S-003", BigDecimal.valueOf(300), customer)
    ));
    return ResponseEntity.ok(customer);
}

Calling this endpoint produces a page filled with repeated JSON fragments and a console warning about exceeding the maximum recursion depth of 1000.

Solution 1: @JsonManagedReference / @JsonBackReference

Mark the parent side with @JsonManagedReference and the child side with @JsonBackReference to break the cycle during serialization.

public class Customer {
    @JsonManagedReference
    private List<Order> orders = new ArrayList<>();
}

public class Order {
    @JsonBackReference
    private Customer customer;
}

After applying the annotations, the endpoint returns the expected JSON where the Customer field inside Order is omitted.

The annotations can be swapped, producing the same effect.

public class Customer {
    @JsonBackReference
    private List<Order> orders = new ArrayList<>();
}

public class Order {
    @JsonManagedReference
    private Customer customer;
}

Solution 2: @JsonIdentityInfo

Use @JsonIdentityInfo to serialize objects by a unique identifier, preventing repeated serialization of the same instance.

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Customer {
    private Long id;
    private List<Order> orders = new ArrayList<>();
}

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Order {
    private Long id;
    private Customer customer;
}

The resulting JSON shows only the IDs for linked objects.

Solution 3: @JsonIgnore

Apply @JsonIgnore to the field that should be excluded from both serialization and deserialization.

public class Order {
    @JsonIgnore
    private Customer customer;
}

After adding the annotation, the JSON output no longer contains the back‑reference.

Solution 4: @JsonView

Define view classes to control which fields are serialized for different API scenarios.

public class Views {
    public static class Public {}
    public static class Internal extends Public {}
}

public class Customer {
    @JsonView(Views.Public.class)
    private Long id;
    @JsonView(Views.Public.class)
    private String name;
    @JsonView(Views.Internal.class)
    private List<Order> orders = new ArrayList<>();
}

public class Order {
    @JsonView(Views.Public.class)
    private Long id;
    @JsonView(Views.Public.class)
    private String sno;
    @JsonView(Views.Public.class)
    private BigDecimal total;
    @JsonView(Views.Public.class)
    private Customer customer;
}

Annotate controller methods with @JsonView(Views.Public.class) to return only the public fields.

Solution 5: Custom Serializer

Implement a custom serializer for the collection to output only the IDs of the related Order objects.

public class CustomListSerializer extends StdSerializer<List<Order>> {
    public CustomListSerializer() { this(null); }
    public CustomListSerializer(Class<List<Order>> t) { super(t); }
    @Override
    public void serialize(List<Order> orders, JsonGenerator gen, SerializerProvider provider) throws IOException {
        List<Long> ids = new ArrayList<>();
        for (Order order : orders) {
            ids.add(order.getId());
        }
        gen.writeObject(ids);
    }
}

Apply it to the orders field:

public class Customer {
    @JsonSerialize(using = CustomListSerializer.class)
    private List<Order> orders = new ArrayList<>();
}

The endpoint now returns a list of order IDs instead of full objects.

Conclusion

These six Jackson-based techniques—@JsonManagedReference/@JsonBackReference, @JsonIdentityInfo, @JsonIgnore, @JsonView, and custom serializers—provide flexible ways to prevent infinite recursion when serializing bidirectional relationships in Spring Boot applications.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendJavaSpring BootJacksonBidirectional relationshipJSON recursion
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.