Привет У меня есть два простых класса в двунаправленной взаимосвязи, которые вызывают бесконечную рекурсию, приводящую к возможному переполнению стека при сериализации в JSON. Я использую Spring Boot 2.2.2
стартовый проект для Couchbase
и Spring Web зависимостей. Мой файл maven pom.xml
выглядит следующим образом:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.stevie</groupId>
<artifactId>stack-overflow-error</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>stack-overflow-error</name>
<description>Reproduce Couchbase API Failure</description>
<properties>
<java.version>13</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-couchbase</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Классы моего домена следующие:
Домашнее хозяйство. java
import org.springframework.data.couchbase.core.mapping.Document;
import com.couchbase.client.java.repository.annotation.Field;
import com.couchbase.client.java.repository.annotation.Id;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
@Document
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "@id")
public final class Household {
@Id
private final String key;
@Field
private String type = "household";
@Field
private final String headSurname;
@Field
private final String headForename;
@Field
private final List<Member> members;
public static Household of(String key, String headSurname, String headForename) {
return new Household(key, headSurname, headForename);
}
Household(String key, String headSurname, String headForename) {
super();
this.key = key;
this.headSurname = headSurname;
this.headForename = headForename;
this.members = new ArrayList<>();
}
public String getKey() {
return key;
}
public String getHeadSurname() {
return headSurname;
}
public String getHeadForename() {
return headForename;
}
@JsonManagedReference
public List<Member> getMembers() {
return members;
}
@Override
public String toString() {
return String.format("Household [key=%s, headSurname=%s, headForename=%s]", key,
headSurname, headForename);
}
}
Участник. java
import org.springframework.data.couchbase.core.mapping.Document;
import com.couchbase.client.java.repository.annotation.Field;
import com.couchbase.client.java.repository.annotation.Id;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
@Document
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "@id")
public final class Member {
@Id
private final String key;
@Field
private final String name;
@Field
private final Household household;
public static Member of(String key, String name, Household household) {
return new Member(key, name, household);
}
Member(String key, String name, Household household) {
super();
this.key = key;
this.name = name;
this.household = household;
}
public String getKey() {
return key;
}
public String getName() {
return name;
}
@JsonBackReference
public Household getHousehold() {
return household;
}
@Override
public String toString() {
return String.format(
"Member [key=%s, name=%s]", key, name);
}
}
Мой модульный тест (JUnit 5):
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.stevie.overflow.StackOverflowErrorApplication;
import org.stevie.overflow.domain.Household;
import org.stevie.overflow.domain.Member;
import com.couchbase.client.java.Bucket;
@SpringBootTest(classes = StackOverflowErrorApplication.class)
class HouseholdRepositoryTest {
@Autowired
private HouseholdRepository repo;
@Autowired
private CouchbaseTemplate template;
@BeforeAll
static void setUpBeforeClass() throws Exception {
}
@AfterAll
static void tearDownAfterClass() throws Exception {
}
@BeforeEach
void setUp() throws Exception {
}
@AfterEach
void tearDown() throws Exception {
}
@Test
void throwsStackOverflowExceptionTest() {
String key = manuallyGenerateKey("household");
Household house = Household.of(key, "BLOGGS", "Joe");
key = manuallyGenerateKey("member");
Member m1 = Member.of(key, "Joe Bloggs", house);
house.getMembers().add(m1);
/** Following code throws Stack overflow exception!!!!! **/
house = repo.save(house);
}
private String manuallyGenerateKey(String prefix) {
Bucket bucket = template.getCouchbaseBucket();
Long nextId = bucket.counter("counter::" + prefix, 1, 100).content();
String key = prefix + "::" + nextId;
return key;
}
}
Я хочу, чтобы JSON для домохозяйства содержало его объекты-члены, а член содержал его объект-домохозяйство. Я пробовал аннотации Джексона, такие как @JsonManagedReference
и @JsonIdentityInfo
, но Spring, кажется, игнорирует их. Благодаря.