Java toJson()在Servlet中抛出StackOverflowerError

Java toJson()在Servlet中抛出StackOverflowerError,java,angularjs,json,servlets,jakarta-ee,Java,Angularjs,Json,Servlets,Jakarta Ee,我有一个对象列表 List<Client> clientsList=new ArrayList<Client>(); clientsList=clientDao.GetAllClients(); 我得到这个错误: java.lang.StackOverflowError at com.google.gson.stream.JsonWriter.beforeName(JsonWriter.java:603) at com.google.gson.stream.JsonWr

我有一个对象列表

List<Client> clientsList=new ArrayList<Client>();
clientsList=clientDao.GetAllClients();
我得到这个错误:

java.lang.StackOverflowError
at com.google.gson.stream.JsonWriter.beforeName(JsonWriter.java:603)
at com.google.gson.stream.JsonWriter.writeDeferredName(JsonWriter.java:401)
at com.google.gson.stream.JsonWriter.value(JsonWriter.java:512)
at com.google.gson.internal.bind.TypeAdapters$8.write(TypeAdapters.java:270)
at com.google.gson.internal.bind.TypeAdapters$8.write(TypeAdapters.java:255)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:113)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:240)

这是因为您的实体具有双向连接。例如,
Client
有一组
Rent
s,每个Rent都有一个对
Client
的引用。当您尝试序列化
客户机时,您将序列化其
租金
,然后必须序列化
租金
中的每个
客户机
,依此类推。这就是导致堆栈溢出错误的原因

要解决此问题,您必须将某些属性标记为
transient
(或使用类似的标记),例如在
Rent
中使用
transient Client
,则任何编组库都将忽略此属性

对于Gson,您可以通过另一种方式标记那些您想要包含在json中的字段,使用
@Expose
,并使用以下内容创建Gson对象:

Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();

另外,我想提到的是,将JPA实体转换为json并将其发送到某处通常不是一个好主意。我建议创建一个DTO(数据传输对象)类,其中只包含所需的信息,最好只使用简单的类型,如
int
Date
String
等等。如果您对这种方法有疑问,可以在谷歌上搜索
DTO
数据传输对象
,或者点击以下链接:

经过一段时间的努力,我相信我已经找到了解决方案。 正如@Nestor Sokil所解释的,问题在于未解析的双向连接,以及在序列化连接时如何表示连接。 修复该行为的方法是“告诉”
gson
如何序列化对象。为此,我们使用
适配器

通过使用
适配器
我们可以告诉
gson
如何序列化
实体
类中的每个属性以及要序列化的属性

Foo
Bar
为两个实体,其中
Foo
Bar
one
关系,而
Bar
Foo有
manytone
关系。我们定义了
Bar
适配器,因此当
gson
序列化
Bar
时,从
Bar
循环引用的角度定义如何序列化
Foo
将不可能

public class BarAdapter implements JsonSerializer<Bar> {
    @Override
    public JsonElement serialize(Bar bar, Type typeOfSrc, JsonSerializationContext context) {
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("id", bar.getId());
        jsonObject.addProperty("name", bar.getName());
        jsonObject.addProperty("foo_id", bar.getFoo().getId());
        return jsonObject;
    }
}
还有一件事需要澄清,
foo_id
不是强制性的,可以跳过。本例中适配器的目的是序列化
Bar
,通过放置
foo\u id
我们表明
Bar
可以触发
manytone
,而不会导致
foo
再次触发
OneToMany


答案基于个人经验,所以请随意评论、证明我错了、纠正错误或扩展答案。无论如何,我希望有人会觉得这个答案很有用。

非常感谢@Nestor Sokil的有用回复
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
public class BarAdapter implements JsonSerializer<Bar> {
    @Override
    public JsonElement serialize(Bar bar, Type typeOfSrc, JsonSerializationContext context) {
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("id", bar.getId());
        jsonObject.addProperty("name", bar.getName());
        jsonObject.addProperty("foo_id", bar.getFoo().getId());
        return jsonObject;
    }
}
public String getSomething() {
    //getRelevantFoos() is some method that fetches foos from database, and puts them in list
    List<Foo> fooList = getRelevantFoos();

    GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.registerTypeAdapter(Bar.class, new BarAdapter());
    Gson gson = gsonBuilder.create();

    String jsonResponse = gson.toJson(fooList);
    return jsonResponse;
}