Could not write JSON: Infinite recursion (StackOverflowError); nested exception spring boot
Asked Answered
I

14

39

This is my District Controller, when I try to fetch data after saving I get the error, even when I try get object form getDistrict(Long id) the same strikes please suggest some way, am very new at spring environment:

    package com.gad.services;

    import java.util.List;

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;

    import com.gad.repositories.DistrictMasterRepositories;
    import com.gad.rmodels.Districtmaster;
    import com.gad.rmodels.Statemaster;

    @Service
    public class DistricMasterServices {

        @Autowired
        DistrictMasterRepositories districtMasterRepositories;
        @Autowired
        StateMasterServices stateMasterServices;
        List<Districtmaster> districtmaster;

        public Iterable<Districtmaster> savenewdistrict(Long id,Districtmaster districtmaster_rec){
             System.out.println(id);
             Statemaster statemaster=null;
             statemaster = stateMasterServices.getStateById(id);
             System.out.println("savenewdistrict");



                districtmaster_rec.setStatemaster(statemaster);
                districtMasterRepositories.save(districtmaster_rec);
                    Iterable<Districtmaster>districtmaster2 = districtMasterRepositories.findAll();
                    return  districtmaster2;


        }


        public Districtmaster  getDistrict(Long id){
            Districtmaster districtmaster =  districtMasterRepositories.findOne(id);
            return districtmaster;

        }
    }

The model class for state:

 package com.gad.rmodels;
    import static javax.persistence.GenerationType.SEQUENCE;
    import java.util.HashSet;
    import java.util.Set;
    import javax.persistence.CascadeType;
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.FetchType;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.OneToMany;
    import javax.persistence.SequenceGenerator;
    import javax.persistence.Table;

    /**
     * Statemaster generated by hbm2java
     */
    @Entity
    @Table(name="statemaster"
        ,schema="aop_gad_v1"
    )
    public class Statemaster  implements java.io.Serializable {


         private long id;
         private String stateName;
         private Set<Districtmaster> districtmasters = new HashSet<Districtmaster>(0);

        public Statemaster() {
        }


        public Statemaster(long id) {
            this.id = id;
        }
        public Statemaster(long id, String stateName, Set<Districtmaster> districtmasters) {
           this.id = id;
           this.stateName = stateName;
           this.districtmasters = districtmasters;
        }



        @SequenceGenerator(name="generator_statemasterid", sequenceName="aop_gad_v1.gad_statemaster_seq")
        @Id 
        @GeneratedValue(strategy=SEQUENCE, generator="generator_statemasterid")
        @Column(name="id", unique=true, nullable=false)
        public long getId() {
            return this.id;
        }

        public void setId(long id) {
            this.id = id;
        }

        @Column(name="state_name", length=20)
        public String getStateName() {
            return this.stateName;
        }

        public void setStateName(String stateName) {
            this.stateName = stateName;
        }

    @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="statemaster")
        public Set<Districtmaster> getDistrictmasters() {
            return this.districtmasters;
        }

        public void setDistrictmasters(Set<Districtmaster> districtmasters) {
            this.districtmasters = districtmasters;
        }




    }

Distric model:

package com.gad.rmodels;


import static javax.persistence.GenerationType.SEQUENCE;

import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

/**
 * Districtmaster generated by hbm2java
 */
@SuppressWarnings("serial")
@Entity
@Table(name="districtmaster",schema="aop_gad_v1")
public class Districtmaster  implements java.io.Serializable {


     private long id;
     private Statemaster statemaster;
     private String districtName;
     private Set<GadGuestHouseMaster> gadGuestHouseMasters = new HashSet<GadGuestHouseMaster>(0);

    public Districtmaster() {
    }


    public Districtmaster(long id) {
        this.id = id;
    }
    public Districtmaster(long id, Statemaster statemaster, String districtName, Set<GadGuestHouseMaster> gadGuestHouseMasters) {
       this.id = id;
       this.statemaster = statemaster;
       this.districtName = districtName;
       this.gadGuestHouseMasters = gadGuestHouseMasters;
    }


     @SequenceGenerator(name="generator_districtmasterid", sequenceName="aop_gad_v1.gad_districtmasterid_seq")
     @Id 
     @GeneratedValue(strategy=SEQUENCE, generator="generator_districtmasterid")
     @Column(name="id", unique=true, nullable=false)
    public long getId() {
        return this.id;
    }

    public void setId(long id) {
        this.id = id;
    }
@ManyToOne(fetch=FetchType.LAZY)

    @JoinColumn(name="district_of_state")
    public Statemaster getStatemaster() {
        return this.statemaster;
    }

    public void setStatemaster(Statemaster statemaster) {
        this.statemaster = statemaster;
    }

    @Column(name="district_name", length=20)
    public String getDistrictName() {
        return this.districtName;
    }

    public void setDistrictName(String districtName) {
        this.districtName = districtName;
    }
    @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="districtmaster")
    public Set<GadGuestHouseMaster> getGadGuestHouseMasters() {
        return this.gadGuestHouseMasters;
    }

    public void setGadGuestHouseMasters(Set<GadGuestHouseMaster> gadGuestHouseMasters) {
        this.gadGuestHouseMasters = gadGuestHouseMasters;
    }




}

The Error I get:

[{"timestamp":1512641978311,"status":200,"error":"OK","exception":"org.springframework.http.converter.HttpMessageNotWritableException","message":"Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: com.gad.rmodels.Statemaster[\"districtmasters\"]->org.hibernate.collection.internal.PersistentSet[0]-

Incommutable answered 7/12, 2017 at 10:37 Comment(1)
B
77

You are facing this issue because the Statemaster model contains the object of Districtmaster model, which itself contains the object of Statemaster model. This causes an infinite json recursion.

You can solve this issue by 3 methods.

1 - Create a DTO and include only the fields that you want to display in the response.

2 - You can use the @JsonManagedReference and @JsonBackReference annotations.

E.g. Add the @JsonManagedReference annotation to the Statemaster model.

@JsonManagedReference
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="statemaster")
public Set<Districtmaster> getDistrictmasters() {
    return this.districtmasters;
}

Add the @JsonBackReference annotation to the Districtmaster model.

@JsonBackReference
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="district_of_state")
public Statemaster getStatemaster() {
    return this.statemaster;
}

3 - You can use the @JsonIgnore annotation on the getter or setter method.

@JsonIgnore
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="statemaster")
public Set<Districtmaster> getDistrictmasters() {
    return this.districtmasters;
}

However, this approach will omit the set of Districtmaster from the response.

Bornie answered 8/12, 2017 at 13:12 Comment(0)
E
24

I've been struggling with the same problem for days, I tried @JsonIgnore, @JsonManagedReference and @JsonBackReference annotation, and even @JsonIdentityInfo annotation and none of them have worked.

If you (the future readers ) are in the same situation, the solution is easier than you expected, simply put @JsonIgnore or @JsonManagedReference / @JsonBackReference on the attribute's getter and not on the attribute itself. And that will do.

Here's a simple example how to do so :

Say we have two classes, Order and Product, and OneToMany relation between them.

Order class

public class Order{
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private String id_order;
  private double price;
  @OneToMany(mappedBy = "order")
  @LazyCollection(LazyCollectionOption.FALSE)
  private List<Product> products
  //constructor, getters & setter 
}

Product Class:

public class Product{
   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private String id_product;
   private String name;
   @ManyToOne
   @JoinColumn(name = "id_order")
   private Order order;
   //consturctor, getters & setters
 }

So in order to use @JsonManagedReference and @JsonBackReference, just add them to the getters as the following :

public class Order{
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private String id_order;
  private double price;
  @OneToMany(mappedBy = "order")
  @LazyCollection(LazyCollectionOption.FALSE)
  private List<Product> products
  //constructor, getters & setter 
  @JsonManagedReference
  public List<Product> getProducts(){
    return products;
}

Product class:

public class Product{
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private String id_product;
  private String name;
  @ManyToOne
  @JoinColumn(name = "id_order")
  private Order order;
  //consturctor, getters & setters
  @JsonBackReference
  public Order getOrder(){
    return order;
  }
 }
Eirena answered 30/5, 2019 at 14:37 Comment(6)
Tried every thing - JsonManagedReference / JsonBackReference on the attributes and even on attribute's getter, tried JsonIdentityInfo but no luck. Dont know what is issue, may be some config but cant figure that out as well. Only jsonIgnore working but that is not my solution. RIP my days spent in debugging it.Sheliasheline
I am using lombok, so how can I used this solution?Cuttlebone
Master, Thank you! I could never have imagined that using that annotation on the entity class could solve my problem; and I still do not understand why or how does it. Could you help me to understand? In my case, I have an entity that is mapped (with an external utility) to a DTO for passing it through layers. That DTO is then mapped to a POJO that is sent as the response of a -spring boot- Rest web service. As you can see, JSon in involved only in the last step, when Spring Boot automatically converts the POJO into a JSon. So, why using this annotation on the entity solves this problem?Instanter
what if you are using lombok? how you put @JsonManagedReference on a getter method?Delmerdelmor
For all those who are wondering how to combine this with Lombok: just provide an explicit getter for the field in question that follows the JavaBean naming convention and place the annotation on top of the explicit method.Bartholomeo
Thank you @Faouzi, glad it worked!Vc
U
12

@JsonBackReference and @JsonManagedReference didn't work for me since I was using a class that represented a join table and the only way to get it to work was to put the @JsonBackReference above the fields representing the linked classes. The net effect was that if I was pulling a record from the join class using JSON, none of the join fields would show up, making the returned information useless.

I'd give examples but I like to avoid long replies. @JsonIdentityInfo explained in the article below provides a short, simple but highly efficient solution.

Solution Preview

Bidirectional relationships and InfiniteRecursion

Unsaddle answered 20/4, 2020 at 18:2 Comment(3)
This solution worked for me. Thank you @josephBlackbird
A life saver man!!. To avoid a run a time exception due spring context failing on empty beans. just customise the Jackson binder and override the default @Bean public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() { ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(mapper); return converter; }Norven
Thank you @Joseph, you saved a lot of my time :)Pyne
O
7

That's because for Statemaster in json the set of Districtmaster's is put. And each Districtmaster has the Statemaster in itself, so it's also put into the json. So that you get the infinite recursion

    @JsonIgnore
    @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, 
    mappedBy="statemaster")
    public Set<Districtmaster> getDistrictmasters() {
        return this.districtmasters;
    }

Adding @JsonIgnore annotation on Set<Districtmaster> will prevent that recursion. You can put the @JsonIgnore at public Statemaster getStatemaster() either.

Obfuscate answered 7/12, 2017 at 11:12 Comment(2)
If I put @JsonIgnore then I don't get associated entity data. How to fixed this ?Cuttlebone
use @Joseph Waweru 's solution should workBlackbird
B
6

This issue occurs when dealing with bi-directional mapping. Use @JsonManagedReferenc and @JsonBackReference in DAO/Entity class

@JsonManagedReference is the forward part of reference – the one that gets serialized normally. @JsonBackReference is the back part of reference – it will be omitted from serialization.

(Read More)

Braga answered 2/11, 2018 at 19:5 Comment(0)
S
3

In the Statemaster class make districtmasters a List<Districtmaster> instead of Set<Districtmaster> and change the getter method accordingly: public List<Districtmaster> getDistrictmasters(). Unfortunately I can not explain why, but this worked for me.

Stronghold answered 6/3, 2020 at 16:37 Comment(0)
R
3

Use List instead of Set. This is how I solved my issue.

Rajewski answered 24/1, 2021 at 6:13 Comment(0)
B
2

If you are using Lombok:

The problem may be due to using Lombok and Set together.

Lombok creates an equals method that is used by Set in Java to determine whether two objects are the same or not. The equals method that Lombok generates calls the equals method of another class, which in turn calls back the equals method of the first class, thus generating infinite recursion.

To solve the problem, you need either to use List instead of Set, or to override the equals and hashCode methods (so Lombok will not generate them).

I've found out this trough another StackOverflow question.

Baroque answered 23/2, 2022 at 21:24 Comment(1)
I don't agree, absolutely. Yes infinite recursion is resolved, but only up to the point, where the object is not traversing over the network. When it start traversing, this JSON related issue will come up. so, baeldung.com/…Bloodletting
D
2

Error message i got in my project

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion

In my case, i also faced this same problem to solve this I just use @JsonIgnore, and it's working fine. Then I replace @JsonIgnore with @JsonBackReference also worked perfectly.

To understand the scenario please check the Project, Spouse, Address, and Employee Class

Address Class

import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIgnore;
@Getter
@Setter
@ToString
@NoArgsConstructor
@EqualsAndHashCode
@Entity
@Table(name = "address")
public class Address {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String line1;
    private String line2;
    private String zipCode;
    private String city;
    private String state;
    private String country;

    @JsonIgnore
    //@JsonBackReference
    @ManyToOne
    private Employee employee;

    public Address(String line1, String line2, String zipCode, String city, String state, String country, Employee employee) {
        this.line1 = line1;
        this.line2 = line2;
        this.zipCode = zipCode;
        this.city = city;
        this.state = state;
        this.country = country;
        this.employee = employee;
    }
}

Employee Class

@Getter
@Setter
@NoArgsConstructor
@ToString
@EqualsAndHashCode
@Entity
@Table(name = "employee")
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID", unique = true, nullable = false)
    private Integer employeeId;
    private String employeeName;
    private String employeeCity;
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "fk_spouse")
    private Spouse spouse;

    @OneToMany(cascade = CascadeType.ALL)
    private List<Address> addresses;

    @ManyToMany(cascade = CascadeType.ALL)
    @JoinTable(name = "employee_project",
            joinColumns = @JoinColumn(name = "fk_employee"),
            inverseJoinColumns = @JoinColumn(name = "fk_project"))
    private List<Project> projects;

    public Employee(String employeeName, String employeeCity, Spouse spouse, List<Address> addresses, List<Project> projects) {
        this.employeeName = employeeName;
        this.employeeCity = employeeCity;
        this.spouse = spouse;
        this.addresses = addresses;
        this.projects = projects;
    }

    public void removeProject(Project project){
        this.projects.remove(project);
        project.getEmployees().remove(project);
    }

    public void addProject(Project project){
        this.projects.add(project);
        project.getEmployees().add(this);
    }
}

Project class

import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.*;

import javax.persistence.*;
import java.util.List;

@Getter
@Setter
@ToString
@EqualsAndHashCode
@NoArgsConstructor
@Entity
@Table(name = "project")
public class Project {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String name;
    private String clientName;

    @JsonIgnore
//    @JsonBackReference
    @ManyToMany(mappedBy = "projects")
    private List<Employee> employees;

    public Project(String name, String clientName, List<Employee> employees) {
        this.name = name;
        this.clientName = clientName;
        this.employees = employees;
    }
}

Spouse Class

import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.*;

import javax.persistence.*;

@Getter
@Setter
@NoArgsConstructor
@ToString
@EqualsAndHashCode
@Entity
@Table(name = "spouse")
public class Spouse {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String mobileNo;
    private Long age;

//    @JsonBackReference
    @JsonIgnore
    @OneToOne(mappedBy = "spouse")
    private Employee employee;

    public Spouse(String name, String mobileNo, Long age, Employee employee) {
        this.name = name;
        this.mobileNo = mobileNo;
        this.age = age;
        this.employee = employee;
    }
}

I know both @JsonIgnore and @JsonManagedReference, @JsonBackReference are used to solve the Infinite recursion (StackOverflowError)

Answer

These are Jackson Annotations.

  1. @JsonIgnore is not created to resolve the Infinite recursion (StackOverflowError) problem, only ignores the property of serialization and deserialization, Just avoid the infinite recursion.

  2. Moreover, @JsonManagedReference and @JsonBackReference are created to handle two ways linkage between fields, if you don't need those properties in the serialization or deserialization process, you can use @JsonIgnore. Otherwise, using the @JsonManagedReference /@JsonBackReference pair is the way to go.

Deice answered 5/12, 2022 at 13:22 Comment(0)
G
1

Looks like your problem is Hibernate relations. When you try to serialize the entity Statemaster the serializer calls serialization of the Districtmaster set which in turn somehow reference the Statemaster again.

There are two possible ways to solve:

  1. Unproxy object
  2. Create DTO (Data Transfer Object) - kind of copy of your entity where all necessary fields should be assigned and return the DTO.
Gerent answered 7/12, 2017 at 11:13 Comment(1)
Please add some example belowBraga
S
1

There are 2 different ways to solve the problem:


  1. You need to implement Serializable on all the @Data classes/DTOs being used, and make sure to generate a private static final long serialVersionUID for the class. Advantage: you may use "lombok" @Data/@Getter/@Setter annotations this way.

  1. Put @JsonIgnore on every getter/setter in all the classes/DTOs being used. Disadvantage: using "lombok" @Data/@Getter/@Setter annotations may be problematic.
Shuttlecock answered 28/7, 2021 at 17:43 Comment(0)
P
1

If you use @JsonIgnore verif if its correctly imported It must imported from

import com.fasterxml.jackson.annotation.JsonIgnore;

It solve for me .

Ponce answered 9/5, 2023 at 11:12 Comment(0)
A
1

I have been stuck on this issue for hours and I have figured out why. This is called a circular dependency. It occurs when two or more components depend on each other and create a loop. For examples I have 2 classes right here. The Posts class


import com.fasterxml.jackson.annotation.JsonIgnore;

import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;

@Entity
public class Posts {

    @Id
    @GeneratedValue
    private Integer id;
    private String message;
    
    @ManyToOne(fetch = FetchType.LAZY)
    private User user;

    public Posts() {
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    @Override
    public String toString() {
        return "Posts [id=" + id + ", message=" + message + "]";
    }

}

The User class

package com.tri.rest.webservices.restfulwebservices.User;

import java.time.LocalDate;
import java.util.List;

import com.fasterxml.jackson.annotation.JsonIgnore;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import jakarta.validation.constraints.Past;
import jakarta.validation.constraints.Size;


@Entity(name="user_detail")
public class User {
    
    @Id
    @GeneratedValue
    private int id;
    

    @Size(min = 2, message = "Name should have atleast 2 charracters. ")
    //@JsonProperty("user_name")
    private String name;
    
    @Past(message = "Birth Date should be in the past. ")
    //@JsonProperty("birth_day")
    private LocalDate birthDate;
    
        
    @OneToMany(mappedBy = "user" )
    private List<Posts> posts;
    
    
    
    public User() {}

    public User(int id, String name, LocalDate birthDate) {
        super();
        this.id = id;
        this.name = name;
        this.birthDate = birthDate;
    }
    
    
    public List<Posts> getPosts() {
        return posts;
    }

    public void setPosts(List<Posts> posts) {
        this.posts = posts;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public LocalDate getBirthDate() {
        return birthDate;
    }

    public void setBirthDate(LocalDate birthDate) {
        this.birthDate = birthDate;
    }

    @Override
    public String toString() {
        return "User [id=" + id + ", name=" + name + ", birthDate=" + birthDate + "]";
    }
    
    
}

And here is my controller

@GetMapping(path = "/jpa/users/{id}/posts")
    public List<Posts> getSpecificUserPost(@PathVariable int id) {
        Optional<User> user = userRepository.findById(id);
        
        if (user.isEmpty())
            throw new UserNotFoundException("Id:" + id);
        
        
        
        return user.get().getPosts();
    } 

As you can see the User class has a relationship one to many with the Posts class. when I printed out the User, the Posts is printed, and when the post is printed, the user is printed, and so on. So I added the @JsonIgnore to the attribute User in the Posts class, it would stop the loop.

@JsonIgnore
    @ManyToOne(fetch = FetchType.LAZY)
    private User user;
Application answered 14/2 at 5:15 Comment(0)
B
0

I too was facing the same issue. I had created a DTO but the thing was while initializing the set I was passing the datatype of entity not the dto. So I changed the datatype of my set from the original entity to the entity's dto and everything started working fine.

Baryton answered 6/1 at 15:41 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Outface

© 2022 - 2024 — McMap. All rights reserved.