gson.toJson() throws StackOverflowError in Servlet

Issue

I have list of objects clients

List<Client> clientsList=new ArrayList<Client>();
clientsList=clientDao.GetAllClients();

Entity Client has others list as attributes:

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

and when i try to convert list of clients to json format

out.write(new Gson().toJson(clientsList));

i get this error :

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)

Solution

That is because your entities have bidirectional connections. So for example Client has a set of Rents and each rent has a reference to Client. When you try serializing a Client you serialize its Rents and then you have to serialize each Client in Rent and so on. This is what causes the StackOverflowError.

To solve this problem you will have to mark some properties as transient (or use some similar anotation), for example use transient Client in Rent Then any marshalling lib will just ignore this property.

In case of Gson you can do the other way around marking those field you do want to be included in json with @Expose and creating the gson object with:

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

P.S. Also, I would like to mention that converting your JPA entity to json and sending it somewhere is generally not a very good idea. I’d recommend creating a DTO(Data Transfer Object) class where you include only the info you need and ideally using only simple types like int, Date, String and so on. If you have questions about this approach you can google for DTO, Data Transfer Object or follow this link: https://www.tutorialspoint.com/design_pattern/transfer_object_pattern.htm

Answered By – Nestor Sokil

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published