高级双向杰克逊序列化以避免无限递归

Posted

技术标签:

【中文标题】高级双向杰克逊序列化以避免无限递归【英文标题】:Advanced bidirection jackson serialization to avoid infinite recusion 【发布时间】:2021-03-10 03:01:19 【问题描述】:

我有一个 Spring Data 项目,它大致有实体设置,我在下面的最小实现中展示了我的问题:

public class JacksonTest 
  @Test
  public void userWithRoles() throws JsonProcessingException 
    User u1 = new User();
    u1.setName("U1");

    Role r1 = new Role();
    r1.setName("R1");

    UserRole ur1 = new UserRole();
    ur1.setRole(r1);
    ur1.setUser(u1);
    ur1.setMetaData("metaData1");

    u1.setRoles(Collections.singletonList(ur1));
    r1.setUsers(Collections.singletonList(ur1));

    String result = new ObjectMapper().writeValueAsString(u1);

    /*
    Expected result
    
    name: U1,
    roles: [metaData: metaData1, role: name: R1]
    
     */

  

  @Test
  public void roleWithUsers() throws JsonProcessingException 
    User u1 = new User();
    u1.setName("U1");

    Role r1 = new Role();
    r1.setName("R1");

    UserRole ur1 = new UserRole();
    ur1.setRole(r1);
    ur1.setUser(u1);
    ur1.setMetaData("metaData1");

    u1.setRoles(Collections.singletonList(ur1));
    r1.setUsers(Collections.singletonList(ur1));

    String result = new ObjectMapper().writeValueAsString(r1);

    /*
    Expected result
    
    name: R1,
    users: [metaData: metaData1, user: name: U1]
    
     */
  


@Data
class UserRole 
  User user;
  Role role;
  String metaData;


@Data
class User 
  String name;
  List<UserRole> roles;


@Data
class Role 
  String name;
  List<UserRole> users;

运行任何一个测试都会导致无限递归。

我曾尝试使用@JsonIgnore@JsonManagedReference@JsonBackReference 来解决我的问题,但它们都不能使两个测试都变成绿色。

我遇到的主要问题是序列化应该给出不同版本的UserRole,这取决于我是序列化User 还是Role,如两个测试中所示。

我的问题是如何使这里的两个测试都与预期的输出一起工作?

【问题讨论】:

在这两种情况下,您都在序列化用户,那么为什么要区分呢? 我的错,我已经更新了这个例子。问题是一样的 【参考方案1】:

写一个自定义JsonSerializer

一个用于List&lt;UserRole&gt; roles 中的User @see UserUserRoleSerializer 和另一个List&lt;UserRole&gt; users in Role @see RoleUserRoleSerializer
@Data
class UserRole 
    User user;
    Role role;
    String metaData;


@Data
class User 
    String name;
    @JsonSerialize(using = UserUserRoleSerializer.class)
    List<UserRole> roles;


@Data
class Role 
    String name;
    @JsonSerialize(using = RoleUserRoleSerializer.class)
    List<UserRole> users;


class RoleUserRoleSerializer extends JsonSerializer<List<UserRole>>  

    @Override
    public void serialize(List<UserRole> value, JsonGenerator gen, SerializerProvider provider) throws IOException 
        gen.writeStartArray();
        for (UserRole userRole : value) 
            gen.writeStartObject();
            gen.writeObjectField("role", userRole.role.name);
            gen.writeObjectField("metadata", userRole.metaData);
            gen.writeEndObject();
        
        gen.writeEndArray();
    


class UserUserRoleSerializer extends JsonSerializer<List<UserRole>> 

    @Override
    public void serialize(List<UserRole> value, JsonGenerator gen, SerializerProvider provider) throws IOException 
        gen.writeStartArray();
        for (UserRole userRole : value) 
            gen.writeStartObject();
            gen.writeObjectField("user", userRole.user.name);
            gen.writeObjectField("metadata", userRole.metaData);
            gen.writeEndObject();
        
        gen.writeEndArray();
    

这将输出:

userWithRoles:"name":"U1","roles":["user":"U1","metadata":"metaData1"]

roleWithUsers:"name":"R1","users":["role":"R1","metadata":"metaData1"]

【讨论】:

以上是关于高级双向杰克逊序列化以避免无限递归的主要内容,如果未能解决你的问题,请参考以下文章

使用杰克逊将双向 JPA 实体序列化为 JSON

杰克逊:冲突的@JsonTypeInfo和@JsonSerialize(as = Klass.class)

杰克逊与 Spring Boot 基于深度的序列化

杰克逊映射无限递归。春季数据休息

我如何反序列化以杰克逊为单位的时间戳?

中间表上杰克逊的无限递归