Gson.toJson() выбрасывает StackOverflowError в сервлете

У меня есть список объектов клиентов

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

Клиент Entity имеет другие списки в качестве атрибутов:

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

Ответ 1

Это потому, что ваши объекты имеют двунаправленные соединения. Так, например, Client имеет набор Rent, и каждая рента имеет ссылку на Client. Когда вы пытаетесь выполнить сериализацию Client, вы сериализуете его Rents, а затем вы должны сериализовать каждый Client в Rent и так далее. Вот что вызывает StackOverflowError.

Чтобы решить эту проблему, вам нужно будет пометить некоторые свойства как transient (или использовать некоторую аналогичную анотацию), например использовать transient Client в Rent. Тогда любая marshalling lib просто проигнорирует это свойство.

В случае Gson вы можете сделать другой способ пометить это поле: сделать хотите включить в json с помощью @Expose и создать объект gson с помощью

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

P.S. Кроме того, я хотел бы упомянуть, что преобразование вашего объекта JPA в json и отправка его где-то, как правило, не очень хорошая идея. Я бы рекомендовал создать класс DTO (Data Transfer Object), в котором вы включаете только необходимую информацию и в идеале используете только простые типы, такие как int, Date, String и т.д. Если у вас есть вопросы об этом подходе, вы можете google для DTO, Data Transfer Object или по этой ссылке: https://www.tutorialspoint.com/design_pattern/transfer_object_pattern.htm