Jackson mixin 在序列化和反序列化时被忽略

Jackson mixin is ignored on serialization and deserialization

当我只有一个无法更改的接口时,我需要能够从 JSON 对象创建 Java POJO。我希望 Mixins 可以帮助实现这一目标。我创建了一个 Mixin,希望它能工作,但无法让 Jackson 使用它。

Jackson 似乎忽略了我为接口和实现定义的 Mixin。如果没有将 Mixin 添加到 ObjectMapper,测试失败是我所期望的。

下面是说明问题的最简单示例。 类 每个都在自己的包中。真正的用例要复杂得多,包括接口列表。我正在使用 Jackson 2.10.3。

对我做错了什么有什么建议吗?

蒂莫西

什么不起作用

接口 reader 测试失败并出现 InvalidDefinitionException:无法构造 model.Level4 的实例(不存在 Creator,如默认构造):抽象类型需要映射到具体类型,具有自定义反序列化器,或包含额外的类型信息

次要的是,Mixin 为名称字段定义了一个新标签 (nameTest),该标签应反映在 writeValueAsString 的输出中。它输出具有标签(名称)原始值的字段。

界面

public interface Level4 {
    public Long getId();    
    public void setId(Long id);    
    public String getName();    
    public void setName(String name);
}

实施

public class Level4Impl implements Level4 {
    private Long id;
    private String name;

    @Override
    public Long getId() {
        return id;
    }

    @Override
    public void setId(Long id) {
        this.id = id;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }
}

混音

public abstract class Level4Mixin {
    public Level4Mixin(
        @JsonProperty("id") Long id,
        @JsonProperty("nameTest") String name) { }
}

单元测试

class Level4MixinTest {
    private ObjectMapper mapper;

    @BeforeEach
    void setUp() throws Exception {
        mapper = new ObjectMapper();
        mapper.addMixIn(Level4.class, Level4Mixin.class);
        mapper.addMixIn(Level4Impl.class, Level4Mixin.class);
    }

    @Test
    void test_InterfaceWrite() throws JsonProcessingException {
        Level4 lvl4 = new Level4Impl();
        lvl4.setId(1L);
        lvl4.setName("test");
        String json = mapper.writeValueAsString(lvl4);
        assertNotNull(json);
        assertTrue(json.contains("nameTest"));
    }

    @Test
    void test_InterfaceRead() throws JsonProcessingException {
        String json = "{\"id\":1,\"nameTest\":\"test\"}";
        assertDoesNotThrow(() -> {
            Level4 parsed = mapper.readValue(json, Level4.class);
            assertNotNull(parsed);
        });
    }

    @Test
    void test_ImplWrite() throws JsonProcessingException {
        Level4Impl lvl4 = new Level4Impl();
        lvl4.setId(1L);
        lvl4.setName("test");
        String json = mapper.writeValueAsString(lvl4);
        assertNotNull(json);
        assertTrue(json.contains("nameTest"));
    }

    @Test
    void test_ImplRead() {
        String json = "{\"id\":1,\"nameTest\":\"test\"}";
        assertDoesNotThrow(() -> {
            Level4Impl parsed = mapper.readValue(json, Level4Impl.class);
            assertNotNull(parsed);
        });
    }
}

要在序列化对象时向该对象添加属性,您可以使用 @JsonAppend。例如:

@JsonAppend(attrs = {@JsonAppend.Attr(value = "nameTest")})
public class Level4Mixin {}

测试:

@BeforeEach
void setUp() throws Exception {
    mapper = new ObjectMapper()
            .addMixIn(Level4Impl.class, Level4Mixin.class);
}

@Test
void test_ImplWrite() throws JsonProcessingException {
    Level4Impl lvl4 = new Level4Impl();
    lvl4.setId(1L);
    lvl4.setName("test");

    String json = mapper.writerFor(Level4Impl.class)
            .withAttribute("nameTest", "myValue")
            .writeValueAsString(lvl4);

    assertNotNull(json);
    assertTrue(json.contains("nameTest"));
    assertTrue(json.contains("myValue"));
}

同样适用于 test_InterfaceWrite

将 json 反序列化为对象的测试不清楚:

@Test
void test_ImplRead() {
    String json = "{\"id\":1,\"nameTest\":\"test\"}";
    assertDoesNotThrow(() -> {
        Level4Impl parsed = mapper.readValue(json, Level4Impl.class);
        assertNotNull(parsed);
    });
}

class Level4Impl 没有 属性 nameTest 所以反序列化失败。如果您不想抛出异常,您可以将 ObjectMapper 配置为不在未知属性上失败。例如:

@Test
void test_ImplRead() {
    String json = "{\"id\":1,\"nameTest\":\"test\"}";
    assertDoesNotThrow(() -> {
        Level4Impl parsed = new ObjectMapper()
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                .readValue(json, Level4Impl.class);
        assertNotNull(parsed);
    });
}

首先你必须让 Jackson 知道它应该实例化你接口的哪个子class。您可以通过向混合 class 添加 @JsonTypeInfo and/or @JsonSubTypes 注释来实现。对于单个 subclass 以下内容就足够了:

@JsonTypeInfo(use = Id.NAME, defaultImpl = Level4Impl.class)
public abstract class Level4Mixin {
}

对于多个子classes,它会稍微复杂一些,并且需要在 JSON 有效负载中添加额外的字段来识别具体类型。有关详细信息,请参阅 Jackson Polymorphic Deserialization。另外值得一提的是,添加类型信息会导致类型 ID 字段被写入 JSON。仅供参考。

添加新标签就像为所需的 属性 添加一对 getter 和 setter 一样简单。显然,在这种情况下,原始 name 字段也将写入 JSON。要更改它,您可能需要将 @JsonIgnore 放在 subclass 或 mix-in 中的 getter 上。在后一种情况下,所有子 classes.

的名称将被忽略

最后注意:在这种情况下,您应该仅使用超级类型注册您的混入。

以下是满足您测试的 classes 的更改:

Level4Impl

public class Level4Impl implements Level4 {

    private Long id;
    private String name;

    @Override
    public Long getId() {
        return id;
    }

    @Override
    public void setId(Long id) {
        this.id = id;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }

    public String getNameTest() {
        return name;
    }

    public void setNameTest(String name) {
        this.name = name;
    }

}

混音

@JsonTypeInfo(use = Id.NAME, defaultImpl = Level4Impl.class)
public interface Level4Mixin {

    @JsonIgnore
    String getName();
}

Level4MixinTest 改变

@BeforeEach
void setUp() throws Exception {
    mapper = new ObjectMapper();
    mapper.addMixIn(Level4.class, Level4Mixin.class);
    // remove 
    //mapper.addMixIn(Level4Impl.class, Level4Mixin.class);
}