我有对象客户列表
List<Client> clientsList=new ArrayList<Client>();
clientsList=clientDao.GetAllClients();
实体客户端将其他列表作为属性:
@ManyToOne(optional=false)
private User createdBy;
@ManyToMany(mappedBy = "Clients")
private Set<ClientType> Types=new HashSet();
@ManyToOne(optional=false)
private LeadSource id_LeadSource;
@ManyToOne(optional=false)
private Agencie id_Agencie;
@OneToMany(cascade=CascadeType.ALL,mappedBy="Owner")
private Set<Propertie> properties=new HashSet();
@OneToMany(cascade=CascadeType.ALL,mappedBy="buyer")
private Set<Sale> sales=new HashSet();
@OneToMany(cascade=CascadeType.ALL,mappedBy = "client")
private Set<Rent> Rents=new HashSet();
@OneToMany(cascade=CascadeType.ALL,mappedBy = "clientDoc")
private Set<Document> Docuements=new HashSet();
当我尝试将客户端列表转换为json格式时
out.write(new Gson().toJson(clientsList));
我收到此错误:
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)
答案 0 :(得分:11)
这是因为您的实体具有双向连接。因此,例如Client
有一组Rent
s,每个租金都有Client
的引用。当您尝试序列化Client
序列化其Rents
时,您必须序列化Client
中的每个Rent
,依此类推。这是导致StackOverflowError
。
要解决此问题,您必须将某些属性标记为transient
(或使用一些类似的anotation),例如在transient Client
中使用Rent
然后任何编组库都会忽略此属性。
在Gson的情况下,你可以用另一种方式来标记那些做想要包含在@Expose
的json中并使用以下内容创建gson对象的字段:
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
P.S。另外,我想提一下将JPA实体转换为json并将其发送到某个地方通常不是一个好主意。我建议您创建一个DTO(数据传输对象)类,其中只包含您需要的信息,理想情况下只使用int
,Date
,String
等简单类型。如果您对此方法有疑问,可以google DTO
,Data Transfer Object
或点击以下链接:https://www.tutorialspoint.com/design_pattern/transfer_object_pattern.htm
答案 1 :(得分:0)
经过一段时间解决此问题后,我相信我有解决方案。
正如@Nestor Sokil所解释的那样,问题在于未解决的双向连接,以及在序列化连接时如何表示连接。
解决该问题的方法是“告诉” gson
如何序列化对象。为此,我们使用Adapters
。
通过使用Adapters
,我们可以告诉gson
如何序列化Entity
类中的每个属性以及要序列化的属性。
让Foo
和Bar
是两个实体,其中Foo
与OneToMany
具有Bar
关系,而Bar
具有ManyToOne
关系到Foo
。我们定义了Bar
适配器,因此当gson
序列化Bar
时,通过从Foo
循环引用的角度定义如何序列化Bar
,将是不可能的。
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
用来表示Foo
实体,该实体将被序列化并导致我们的循环引用问题。现在,当我们使用适配器Foo
时,将不会再次从Bar
对其进行序列化,只会获取其ID并将其放入JSON
中。
现在我们有了Bar
适配器,可以使用它来序列化Foo
。这是想法:
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;
}
还有一点要说明,foo_id
不是强制性的,可以跳过。在此示例中,适配器的目的是序列化Bar
,通过放置foo_id
,我们证明Bar
可以触发ManyToOne
而不会导致Foo
触发{{1} } ...
答案基于个人经验,因此可以随时发表评论,证明我是错误的,纠正错误或扩大答案。无论如何,我希望有人会觉得这个答案有用。