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;
}