将对象列表转换为Json字符串时出现Stackoverflow错误

4

我有一个Java RESTapi,在这里我想将我的自定义Pet对象的列表转换为Json,并在一个端点中显示它。

目前为止,我的代码如下:

@Path("/allPets")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getPetsfromCollection() {
    List<Pet> petList = new ArrayList<>();
    petList.addAll(facade.returnAllPets());

    String json = gson.toJson(petList);

    //TODO return proper representation object
     return Response.ok().entity(json).build();
}

我正在使用门面模式,其中我有一个方法将Java实体添加到列表中,如下所示:

EntityManagerFactory emf = Persistence.createEntityManagerFactory("PetHospitaljpa");


public Collection<Pet> returnAllPets (){

    EntityManager  em = emf.createEntityManager();
    //vi laver en typed query for at specificere hvilken datatype, 
    // det er vi leder efter, i dette tilfælde er det en Pet
    TypedQuery<Pet> query = em.createNamedQuery("Pet.findAll", Pet.class);
    return query.getResultList();
}

我返回一个集合,以便以后可以将ArrayList的数据结构更改为其他内容。

我尝试了几种解决方法,但仍然出现堆栈溢出错误。

我知道需要使用DTO代替,因此我已经创建了一个自定义方法将实体更改为DTO,如下所示:

public static DTOPet converttoDTO(Pet entity){

   DTOPet dto = new DTOPet();
   dto.setId(entity.getId());
   dto.setName(entity.getName());
   dto.setBirth(entity.getBirth());
   dto.setDeath(entity.getDeath());
   dto.setSpecies(entity.getSpecies());

   return dto;
}

我不确定是否这是良好的代码实践,如果有其他方法可以将实体集合转换为DTO,请告诉我?

如前所述。问题发生是因为我有循环引用。

在我的Pet Entity类中:

@ManyToOne
private Owner ownerId;

在我的所有者实体类中:

@OneToMany(mappedBy = "ownerId")
private Collection<Pet> petCollection;

我的宠物课:

    /*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package Entities;

import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;

/**
 *
 * @author kristoffer
 */
@Entity
@Table(name = "pet")
@XmlRootElement
@NamedQueries({
    @NamedQuery(name = "Pet.findAll", query = "SELECT p FROM Pet p")
    , @NamedQuery(name = "Pet.findById", query = "SELECT p FROM Pet p WHERE p.id = :id")
    , @NamedQuery(name = "Pet.findByName", query = "SELECT p FROM Pet p WHERE p.name = :name")
    , @NamedQuery(name = "Pet.findByBirth", query = "SELECT p FROM Pet p WHERE p.birth = :birth")
    , @NamedQuery(name = "Pet.findBySpecies", query = "SELECT p FROM Pet p WHERE p.species = :species")
    , @NamedQuery(name = "Pet.findByDeath", query = "SELECT p FROM Pet p WHERE p.death = :death")})
public class Pet implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id")
    private Integer id;
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 45)
    @Column(name = "name")
    private String name;
    @Basic(optional = false)
    @NotNull
    @Column(name = "birth")
    @Temporal(TemporalType.DATE)
    private Date birth;
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 45)
    @Column(name = "species")
    private String species;
    @Column(name = "death")
    @Temporal(TemporalType.DATE)
    private Date death;
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "petId")
    private Collection<Event> eventCollection;
    @JoinColumn(name = "owner_id", referencedColumnName = "id")
    @ManyToOne
    private Owner ownerId;

    public Pet() {
    }

    public Pet(Integer id) {
        this.id = id;
    }

    public Pet(Integer id, String name, Date birth, String species) {
        this.id = id;
        this.name = name;
        this.birth = birth;
        this.species = species;
    }

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }

    public String getSpecies() {
        return species;
    }

    public void setSpecies(String species) {
        this.species = species;
    }

    public Date getDeath() {
        return death;
    }

    public void setDeath(Date death) {
        this.death = death;
    }

    @XmlTransient
    public Collection<Event> getEventCollection() {
        return eventCollection;
    }

    public void setEventCollection(Collection<Event> eventCollection) {
        this.eventCollection = eventCollection;
    }

    public Owner getOwnerId() {
        return ownerId;
    }

    public void setOwnerId(Owner ownerId) {
        this.ownerId = ownerId;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (id != null ? id.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof Pet)) {
            return false;
        }
        Pet other = (Pet) object;
        if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "Pet{" + "id=" + id + ", name=" + name + ", birth=" + birth + ", species=" + species + ", death=" + death + ", eventCollection=" + eventCollection + ", ownerId=" + ownerId + '}';
    }   
}

编辑: 我尝试创建一个方法,将所有对象转换为DTO,但在显示时该字符串仍为空:

 @Path("/allPets")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getPetsfromCollection() { 

    //med denne metode skal vi bruge et DTO(data transfer object til at formatere til Json)

    List<Pet> petList = new ArrayList<>();
    List<DTOPet> DTOPetList = new ArrayList<>();

    petList.addAll(facade.returnAllPets());
    for(Pet pet: petList){
        DTOPet dtopet = EntitytoDTO.converttoDTO(pet);
        DTOPetList.add(dtopet);
    }

    String json = gson2.toJson(DTOPetList);
     return Response.ok().entity(json).build();
}

当我使用调试器时,新的列表已成功创建,并且参数正确,但是字符串JSON只是像这样创建的 [{},{},{},{}],尽管我使用了GSON。

列表中有多少项? - dbl
3
你能分享一下堆栈跟踪信息吗?这将有助于检查问题的原因。 - Kamil
@dbi 这个项目有4个条目。 - Kristoffer Tølbøll
@Kamil,如果代码已知,则不会打印任何对象,因为我首先将其转换为字符串(json)。但是,如果我直接将该方法放入响应对象中,我将会收到一个错误。 - Kristoffer Tølbøll
基本上,我认为如果我只将宠物实体的列表序列化为DTO(POJO),那么它就可以正常工作,因为然后我就可以将它们序列化为JSON,而不会出现stackoverflow错误。问题在于实体之间存在关系,这导致了错误。 - Kristoffer Tølbøll
2个回答

1
你需要检测错误发生的位置。我建议添加调试信息,例如:
@Path("/allPets")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getPetsfromCollection() {
    log.debug("getPetsfromCollection start");
    List<Pet> petList = new ArrayList<>(facade.returnAllPets());
    log.debug("petList" + petList.length());

    String json = gson.toJson(petList);
    log.debug("json " + json);

    //TODO return proper representation object
     return Response.ok().entity(json).build();
}


EntityManagerFactory emf = Persistence.createEntityManagerFactory("PetHospitaljpa");
public Collection<Pet> returnAllPets (){
    log.debug("returnAllPets start"); 
    EntityManager  em = emf.createEntityManager();
    log.debug("createNamedQuery start"); 
    TypedQuery<Pet> query = em.createNamedQuery("Pet.findAll", Pet.class);
    log.debug("single result" + query.getSingleResult() ); 

    TypedQuery<Pet> query = em.createNamedQuery("Pet.findAll", Pet.class);
    log.debug("list result" + query.getResultList()); 

    TypedQuery<Pet> query = em.createNamedQuery("Pet.findAll", Pet.class);
    return query.getResultList();
}

顺便提一下,请展示Pet类,可能问题出在这个类上。

更新:我建议也尝试暂时删除:

@OneToMany(cascade = CascadeType.ALL, mappedBy = "petId")
private Collection<Event> eventCollection;

"和/或"
@JoinColumn(name = "owner_id", referencedColumnName = "id")
@ManyToOne
private Owner ownerId;

请检查是否出现了此类SO异常。这似乎是事件或所有者表过大或存在循环依赖关系的情况。


我尝试使用调试器调试自己编写的方法,但字符串仍为空。 - Kristoffer Tølbøll

0

如果没有看到“Pet”类的样子,很难确定问题所在。我怀疑你在Pet类中有另一个类的变量,该类也有一个对宠物类本身的引用(在序列化过程中创建一个循环引用,会导致堆栈溢出)


是的,问题就是这样。让我编辑一下这个问题。 - Kristoffer Tølbøll
我过去只使用过Jackson。根据快速搜索,您可以修改您的GsonBuilder为Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(); 并使用@expose注释来标记您想要在json中呈现的字段(也许有更好的方法)。 - Solace

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接