You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jackson序列化Organization类时如何避免递归且返回一次parent字段?

解决组织实体递归序列化问题,同时返回一级Parent对象

针对你遇到的问题——加@JsonBackReference拿不到parent字段,去掉就触发无限递归,@JsonIdentityInfo又只能返回ID没法构建节点树,给你几个可行的解决方案:

方案1:用@JsonView区分序列化视图

这种方式能灵活控制不同场景下的序列化字段,既能返回当前实体的parent,又不让parent的parent被序列化:

  1. 先定义两个视图接口:
public interface BaseView {}
public interface WithParentView extends BaseView {}
  1. 修改Organization实体类的字段注解:
@Getter
@Setter
@ToString(callSuper = true)
@Entity(name = "organizations")
public class Organization extends BaseEntity {

    // 仅在WithParentView视图下序列化parent,同时忽略parent自身的parent字段避免递归
    @ManyToOne
    @JoinColumn(name = "parent_id", referencedColumnName = "id")
    @OnDelete(action = OnDeleteAction.CASCADE)
    @JsonView(WithParentView.class)
    @JsonIgnoreProperties("parent")
    private Organization parent;

    // 基础字段绑定BaseView,确保所有视图都能序列化这些内容
    @JsonView(BaseView.class)
    @ManyToOne
    @JoinColumn(name = "organization_type_id", referencedColumnName = "id", nullable = false)
    @OnDelete(action = OnDeleteAction.CASCADE)
    @NotNull(message = "organizationType" + AppConstant.NOT_NULL_MESSAGE)
    private OrganizationType organizationType;

    @JsonView(BaseView.class)
    @Column(name = "address_id", nullable = false)
    @NotNull(message = "addressId" + AppConstant.NOT_NULL_MESSAGE)
    @Min(value = AppConstant.MIN_ID_VALUE, message = "addressId" + AppConstant.MIN_ID_MESSAGE)
    private Integer addressId;

    @JsonView(BaseView.class)
    @Column(name = "name", nullable = false)
    @NotNull(message = "name" + AppConstant.NOT_NULL_MESSAGE)
    @NotEmpty(message = "name" + AppConstant.NOT_EMPTY_MESSAGE)
    @Size(max = AppConstant.MAX_STRING_FIELD_LENGTH, message = "name" + AppConstant.STRING_FIELD_SIZE_MESSAGE)
    private String name;

    @JsonView(BaseView.class)
    @Column(name = "phone_number")
    @Size(max = AppConstant.MAX_STRING_FIELD_LENGTH, message = "phoneNumber" + AppConstant.STRING_FIELD_SIZE_MESSAGE)
    private String phoneNumber;

    @JsonView(BaseView.class)
    @OneToMany(mappedBy = "organization", cascade = CascadeType.ALL, orphanRemoval = true)
    @JsonManagedReference("organization-organizationApps")
    private List<OrganizationApp> organizationApps;
}
  1. 在接口方法上指定使用WithParentView视图:
@GetMapping("/{id}")
@JsonView(WithParentView.class)
public ResponseEntity<Organization> getOrganization(@PathVariable Long id) {
    Organization org = organizationService.findById(id);
    return ResponseEntity.ok(org);
}

方案2:自定义DTO(最稳妥的方式)

直接创建数据传输对象,完全控制返回的字段结构,从根源避免递归:

  1. 创建OrganizationDTO:
@Getter
@Setter
public class OrganizationDTO {
    private Long id;
    private String name;
    private String phoneNumber;
    private Integer addressId;
    private OrganizationType organizationType;
    private List<OrganizationApp> organizationApps;
    // 仅包含parent的基础信息,不嵌套parent的parent
    private ParentOrganizationDTO parent;

    // 内部类定义parent的精简DTO
    @Getter
    @Setter
    public static class ParentOrganizationDTO {
        private Long id;
        private String name;
        // 按需添加parent的其他必要字段,不要加parent字段
    }
}
  1. 在业务层实现实体转DTO:
public OrganizationDTO convertToDTO(Organization org) {
    OrganizationDTO dto = new OrganizationDTO();
    dto.setId(org.getId());
    dto.setName(org.getName());
    dto.setPhoneNumber(org.getPhoneNumber());
    dto.setAddressId(org.getAddressId());
    dto.setOrganizationType(org.getOrganizationType());
    dto.setOrganizationApps(org.getOrganizationApps());
    
    if (org.getParent() != null) {
        OrganizationDTO.ParentOrganizationDTO parentDto = new OrganizationDTO.ParentOrganizationDTO();
        parentDto.setId(org.getParent().getId());
        parentDto.setName(org.getParent().getName());
        // 按需设置parent的其他字段
        dto.setParent(parentDto);
    }
    return dto;
}
  1. 接口直接返回DTO:
@GetMapping("/{id}")
public ResponseEntity<OrganizationDTO> getOrganization(@PathVariable Long id) {
    Organization org = organizationService.findById(id);
    OrganizationDTO dto = organizationService.convertToDTO(org);
    return ResponseEntity.ok(dto);
}

方案3:用@JsonIgnoreProperties忽略parent的递归字段

在parent字段上直接忽略它的parent属性,序列化当前实体时,parent对象不会包含自己的parent,避免无限递归:

修改Organization实体的parent字段:

@ManyToOne
@JoinColumn(name = "parent_id", referencedColumnName = "id")
@OnDelete(action = OnDeleteAction.CASCADE)
@JsonIgnoreProperties("parent") // 忽略parent对象中的parent字段
private Organization parent;

这种方式最简单,但灵活性稍差,适合只需要一级parent的场景。

方案4:自定义Json序列化器

如果需要更精细的控制,可以写一个自定义序列化器:

  1. 创建自定义序列化器:
public class OrganizationSerializer extends StdSerializer<Organization> {

    public OrganizationSerializer() {
        this(null);
    }

    public OrganizationSerializer(Class<Organization> t) {
        super(t);
    }

    @Override
    public void serialize(Organization org, JsonGenerator gen, SerializerProvider provider) throws IOException {
        gen.writeStartObject();
        // 序列化基础字段
        gen.writeNumberField("id", org.getId());
        gen.writeStringField("name", org.getName());
        gen.writeStringField("phoneNumber", org.getPhoneNumber());
        gen.writeNumberField("addressId", org.getAddressId());
        gen.writeObjectField("organizationType", org.getOrganizationType());
        gen.writeObjectField("organizationApps", org.getOrganizationApps());
        
        // 序列化parent,但只输出基础信息,不包含parent的parent
        if (org.getParent() != null) {
            gen.writeFieldName("parent");
            gen.writeStartObject();
            gen.writeNumberField("id", org.getParent().getId());
            gen.writeStringField("name", org.getParent().getName());
            // 按需添加parent的其他字段
            gen.writeEndObject();
        }
        gen.writeEndObject();
    }
}
  1. 在实体类上指定使用这个序列化器:
@JsonSerialize(using = OrganizationSerializer.class)
@Getter
@Setter
@ToString(callSuper = true)
@Entity(name = "organizations")
public class Organization extends BaseEntity {
    // 原字段定义不变,去掉之前的JsonBackReference等注解
}

内容的提问来源于stack exchange,提问作者Muhammad Zohaib

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 16:33:17