簡體   English   中英

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

[英]Jackson mixin is ignored on serialization and deserialization

當我只有一個無法更改的接口時,我需要能夠從 JSON object 創建 Java POJO。 我希望 Mixins 可以幫助實現這一點。 我創建了一個 Mixin,希望它可以工作,但無法讓 Jackson 使用它。

似乎 Jackson 忽略了我為接口和實現定義的 Mixin。 如果沒有將 Mixin 添加到 ObjectMapper,測試失敗是我所期望的。

下面是顯示問題的最簡單示例。 這些類都在各自的 package 中。 真正的用例要復雜得多,包括接口列表。 我正在使用 Jackson 2.10.3。

關於我做錯了什么有什么建議嗎?

提摩太

什么不起作用

接口閱讀器測試失敗並出現 InvalidDefinitionException:無法構造 model.Level4 的實例(不存在像默認構造一樣的創建者):抽象類型需要映射到具體類型、具有自定義反序列化器或包含其他類型信息

次要的,Mixin 為 name 字段定義了一個新的 label (nameTest),它應該反映在 writeValueAsString 的 output 中。 它輸出具有 label(名稱)的原始值的字段。

界面

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

要在 object 被序列化時向 object 添加屬性,您可以使用@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 反序列化為 object 的測試尚不清楚:

@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 知道它應該實例化接口的哪個子類。 您可以通過將@JsonTypeInfo和/或@JsonSubTypes注釋添加到您的混合 class 來實現。 對於單個子類,以下就足夠了:

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

對於多個子類,它會稍微復雜一些,並且需要 JSON 有效負載中的附加字段來識別具體類型。 有關詳細信息,請參閱Jackson 多態反序列化 另外值得一提的是,添加類型信息將導致類型 ID 字段寫入 JSON。 JFYI。

添加新的 label 就像為所需屬性添加一對 getter 和 setter 一樣簡單。 顯然,在這種情況下,原始name字段也將寫入 JSON。 要改變這一點,您可能需要將 @JsonIgnore 放在子類或混合中的 getter 上。 在后一種情況下,所有子類的名稱都將被忽略。

最后一點:在這種情況下,您應該僅使用超類型注冊您的混音。

以下是滿足您的測試的類的更改:

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

如果您無法使其默認工作(這是我的情況),請嘗試修改現有的MappingJackson2HttpMessageConverter轉換器,例如這樣做:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.stream()
            .filter(c -> c instanceof MappingJackson2HttpMessageConverter)
            .forEach(c -> {
                MappingJackson2HttpMessageConverter converter = (MappingJackson2HttpMessageConverter) c;
                converter.getObjectMapper().addMixIn(APIResponse.class, MixInAPIResponse.class);
            });
}

其中MixInAPIResponse是您為目標 class ApiResponse 配置的MixIn ApiResponse

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM