首页 > 解决方案 > 将对象列表转换为 Json 字符串时发生 Stackoverflow 错误

问题描述

我有一个 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,我不确定这是否是良好的代码实践?

正如所指出的。出现问题是因为我有一个循环引用。

在我的宠物实体类中:

@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

标签: javajsonrest

解决方案


您需要检测错误的位置。我建议添加调试信息,例如

@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();
}

PS另外,请显示 Pet 类,可能是这个类的问题。

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

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

和/或

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

并检查你是否有这样的例外。看起来事件或所有者表太大或具有圈子依赖性。


推荐阅读