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

ncecgwcz  于 2021-06-27  发布在  Java
关注(0)|答案(2)|浏览(500)

当我只有一个不能更改的接口时,我需要能够从json对象创建javapojo。我希望mixins能帮我实现这一点。我创造了一个混音,希望能工作,但不能让Jackson使用它。
看来jackson忽略了我为接口和实现定义的mixin。如果没有将mixin添加到objectmapper中,测试失败是我所期望的。
下面是显示问题的最简单示例。这些类都在各自的包中。真正的用例要复杂得多,包括接口列表。我用的是Jackson2.10.3。
对我做错了什么有什么建议吗?
提摩太
什么不起作用
接口读取器测试失败,invaliddefinitionexception:无法构造model.level4的示例(与默认构造一样,不存在任何创建者):抽象类型需要Map到具体类型、具有自定义反序列化程序或包含其他类型信息
其次,mixin为name字段定义了一个新标签(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);
        });
    }
}
xqkwcwgp

xqkwcwgp1#

要在序列化对象时向该对象添加属性,可以使用@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);
    });
}

班级 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);
    });
}
vyu0f0g1

vyu0f0g12#

首先,您必须让jackson知道它应该示例化接口的哪个子类。你可以通过添加 @JsonTypeInfo 和/或 @JsonSubTypes 你在课堂上的混音注解。对于单个子类,以下内容就足够了:

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

对于多个子类,它将更加复杂,并且需要json负载中标识具体类型的额外字段。有关详细信息,请参见jackson多态反序列化。另外值得一提的是,添加类型信息将导致type id字段被写入json。仅供参考。
添加新标签就像为所需属性添加一对getter和setter一样简单。显然是原创的 name 在这种情况下,字段也将写入json。为了改变这种情况,您可能需要将@jsonignore放在getter的subclass或mix-in中。在后一种情况下,所有子类的名称都将被忽略。
最后一点:在这种情况下,你应该注册你的混音与超级类型只。
以下是对满足测试要求的类所做的更改:
级别4IMPL

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();
}

级别4混合更改

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

相关问题