Исключение переполнения стека Json (fasterxml)

При попытке сериализовать категорию я получаю переполнение стека.

Исключение

Предупреждение: StandardWrapperValve [диспетчер]: Servlet.service() для диспетчера сервлетов вызвало исключение java.lang.StackOverflowError в java.lang.ClassLoader.defineClass1 (собственный метод) в java.lang.ClassLoader.defineClass (ClassLoader.java:760) в org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.findClass(BundleWiringImpl.java:2279) в org.apache.felix.framework.BundleWiringImpl.findClassOrResourceByDelegation(BundleWiringImpl.java:1501) в org.apache.felix.framework.BundleWiringImpl. access$400(BundleWiringImpl.java:75) в org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.loadClass(BundleWiringImpl.java:1955) в java.lang.ClassLoader.loadClass(ClassLoader.java:357) в com.fasterxml. jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:660) на com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:152) на com.fasterxml.jackson.databind.ser. импл.IndexedList Serializer.serializeContents(IndexedListSerializer.java:100) в com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:21) в com.fasterxml.jackson.databind.ser.std.AsArraySerializerBase.serialize( AsArraySerializerBase.java:183) в com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:541) в com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:644) в com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:152)

Категория.java

@Entity
public class Category implements DataObject, Serializable {

    @Id
    @GeneratedValue 
    private Long id;
    private String title;
    private String description;

    @ManyToOne @JsonIgnore 
    private Category parent;


@Override
public long getId() {
    return id;
}

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

public String getTitle() {
    return title;
}

public void setTitle(String title) {
    this.title = title;
}
public String getDescription() {
    return description;
}

public void setDescription(String description) {
    this.description = description;
}

public Category getParent() {
   return null;//return parent;
}

public void setParent(Category parent) {
   // this.parent = parent;
}

public boolean isMainCategory()
{
   return true;// return this.parent == null;
}

/**
 * Returns the chain of parent categories with the main category on index 0
 * @return Chain of categories 
 */
public List<Category> getParentChain()
{

   List<Category> cats = new ArrayList<>();
    Category current = this;
    while(!current.isMainCategory())
    {
        cats.add(current);
        current = current.getParent();
    }
    cats.add(current);
    Collections.reverse(cats);
    return cats;
}

@Override
public String toString()
{
    return this.title;
}

@Override
public boolean equals(Object o)
{
    if(!(o instanceof Category))return false;
    Category c = (Category)o;

    return c.title.equals(this.title);
}

@Override
public int hashCode()
{
    return super.hashCode();
} 
}

Функция контроллера отдыха

@RequestMapping(value="/{id}", method=RequestMethod.GET)
public ResponseEntity<Category> get(@PathVariable("id") long categoryId)
{
    Category c  =  service.getCategoryRepository().ReadValue(categoryId);
    if(c == null)
        return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    return new ResponseEntity<>(c,HttpStatus.OK);
}

Примечание

Даже когда я заменю return new ResponseEntity<>(c,HttpStatus.OK); на return new ResponseEntity<>(new Category(),HttpStatus.OK);, я получу список stackoverflow, ни одно из полей не содержит значения.

Он отлично работает с другими моими классами, только этот класс вызывает переполнение стека.


person Svexo    schedule 05.08.2015    source источник
comment
Возможный дубликат Spring Expected ':' вместо ошибки 't' при возврате списка?   -  person mohit sharma    schedule 03.11.2016


Ответы (4)


Вероятно, если вы прокомментируете private Category parent;, у вас не будет StackOverflow. У меня такая же проблема в проекте с круговыми зависимостями.

Лучший способ решить эту проблему — использовать идентификатор родителя вместо класса, например:

private Long parentId;

Редактировать:

Проблема с getParentChain(), который пытается сериализоваться. Добавив @JsonIgnore перед методом, проблема была решена.

person JFPicard    schedule 05.08.2015
comment
Я пробовал, но, к сожалению, это тоже не работает. Я сделал чистую сборку, чтобы убедиться, но все равно тот же результат - person Svexo; 05.08.2015
comment
Не могли бы вы указать, что такое «//и некоторые функции»? - person JFPicard; 05.08.2015
comment
Что, если вы прокомментируете getParentChain()? - person JFPicard; 05.08.2015
comment
Ха, это была проблема. Я не знал, что вы можете использовать @JsonIgnore функцией, но, видимо, вы можете. - person Svexo; 05.08.2015
comment
@JsonIgnore public Parent getParent() { return parent; }, ...... решит проблему циклической зависимости. - person EngineSense; 24.06.2016

Конечно, @JsonIgnore делает свою работу. Но что, если нам нужно игнорировать поле в нашем выводе JSON?

Решение очень простое.

Мы аннотируем наше «виновное» поле аннотацией @JsonManagedReference с одной стороны нашего отношения (что означает нашу аннотацию @ManyToMany).

И @JsonBackReference с другой стороны отношения (где @OneToMany было помещено).

Вот и все. Больше никаких рекурсивных циклов.

person Rafal    schedule 20.08.2016
comment
Я думаю, что там, где он говорит @ManyToMany, он должен вместо этого говорить @ManyToOne. - person Paco Abato; 25.08.2018

Одна аннотация решает вашу проблему.

Добавьте следующую аннотацию к классу.

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

Другой способ — аннотировать коллекции @JsonManagedReference для прямого направления и @JsonBackReference. для обратного направления в отображении.

пример:

public class User{
    @JsonManagedReference
    @OneToMany(mappedBy = "user")
    Set<Address> s = new Hashset<>();
}

public class Address{
    @JsonBackReference
    @ManyToOne
    @JoinColumn
    User user;
}
person Shamanth Ramesh    schedule 13.10.2017

Это то, что я делаю, чтобы избежать этого рекурсивного ада.

Добавьте @JsonIgnore к каждому @OneToMany(mappedBy="xxxx") в ваших объектах JPA.

JsonIgnore от jackson-annotations

    <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
        <version>2.10.0</version>
    </dependency>

Пример объекта JPA:

package model;

import java.io.Serializable;
import javax.persistence.*;
import javax.xml.bind.annotation.XmlRootElement;

import com.fasterxml.jackson.annotation.JsonIgnore;

import java.util.List;


/**
 * The persistent class for the categoria database table.
 * 
 */
@Entity
@NamedQuery(name="Categoria.findAll", query="SELECT c FROM Categoria c")
@XmlRootElement(name = "categoria")
public class Categoria implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @Column(name="categoria_id")
    private int categoriaId;

    private String descripcion;

    @JsonIgnore
    //bi-directional many-to-one association to Establecimiento
    @OneToMany(mappedBy="categoria")
    private List<Establecimiento> establecimientos;

    public Categoria() {
    }

    public int getCategoriaId() {
        return this.categoriaId;
    }

    public void setCategoriaId(int categoriaId) {
        this.categoriaId = categoriaId;
    }

    public String getDescripcion() {
        return this.descripcion;
    }

    public void setDescripcion(String descripcion) {
        this.descripcion = descripcion;
    }

    public List<Establecimiento> getEstablecimientos() {
        return this.establecimientos;
    }

    public void setEstablecimientos(List<Establecimiento> establecimientos) {
        this.establecimientos = establecimientos;
    }

    public Establecimiento addEstablecimiento(Establecimiento establecimiento) {
        getEstablecimientos().add(establecimiento);
        establecimiento.setCategoria(this);

        return establecimiento;
    }

    public Establecimiento removeEstablecimiento(Establecimiento establecimiento) {
        getEstablecimientos().remove(establecimiento);
        establecimiento.setCategoria(null);

        return establecimiento;
    }

}
person Wesos de Queso    schedule 26.11.2019