簡體   English   中英

如何在 Jackson 中為泛型類型創建自定義反序列化器?

[英]How to create a custom deserializer in Jackson for a generic type?

想象以下場景:

class <T> Foo<T> {
    ....
}

class Bar {
    Foo<Something> foo;
}

我想為 Foo 編寫一個自定義的 Jackson 反序列化器。 為了做到這一點(例如,為了反序列化具有Foo<Something>屬性的Bar類),我需要在反序列化時知道Bar使用的Foo<T>的具體類型(例如,我需要知道在那個特定的情況下, TSomething )。

如何編寫這樣的解串器? 應該可以做到這一點,因為 Jackson 使用類型化集合和地圖來做到這一點。

說明:

似乎有兩個部分可以解決問題:

1) 在Bar獲取聲明類型的屬性foo並使用它來反序列化Foo<Somehting>

2) 在反序列化時發現我們正在反序列化類Bar中的屬性foo以成功完成步驟 1)

如何完成 1 和 2 ?

你可以為你的泛型類型實現一個自定義的JsonDeserializer ,它也實現了ContextualDeserializer

例如,假設我們有以下包含泛型值的簡單包裝器類型:

public static class Wrapper<T> {
    public T value;
}

我們現在想要反序列化如下所示的 JSON:

{
    "name": "Alice",
    "age": 37
}

進入一個看起來像這樣的類的實例:

public static class Person {
    public Wrapper<String> name;
    public Wrapper<Integer> age;
}

實現ContextualDeserializer允許我們根據字段的泛型類型參數為Person類中的每個字段創建一個特定的反序列化器。 這允許我們將名稱反序列化為字符串,將年齡反序列化為整數。

完整的解串器如下所示:

public static class WrapperDeserializer extends JsonDeserializer<Wrapper<?>> implements ContextualDeserializer {
    private JavaType valueType;

    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
        JavaType wrapperType = property.getType();
        JavaType valueType = wrapperType.containedType(0);
        WrapperDeserializer deserializer = new WrapperDeserializer();
        deserializer.valueType = valueType;
        return deserializer;
    }

    @Override
    public Wrapper<?> deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException {
        Wrapper<?> wrapper = new Wrapper<>();
        wrapper.value = ctxt.readValue(parser, valueType);
        return wrapper;
    }
}

最好先在此處查看createContextual ,因為 Jackson 將首先調用它。 我們從BeanProperty讀取字段的類型(例如Wrapper<String> ),然后提取第一個泛型類型參數(例如String )。 然后我們創建一個新的反序列化器並將內部類型存儲為valueType

一旦在這個新創建的反deserialize上調用 deserialize,我們可以簡單地要求 Jackson 將值反序列化為內部類型而不是整個包裝器類型,並返回一個包含反序列化值的新Wrapper器。

為了注冊這個自定義反序列化器,我們需要創建一個包含它的模塊,並注冊該模塊:

SimpleModule module = new SimpleModule()
        .addDeserializer(Wrapper.class, new WrapperDeserializer());

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(module);

如果我們然后嘗試反序列化上面的示例 JSON,我們可以看到它按預期工作:

Person person = objectMapper.readValue(json, Person.class);
System.out.println(person.name.value);  // prints Alice
System.out.println(person.age.value);   // prints 37

Jackson 文檔中有更多關於上下文反序列化器如何工作的詳細信息。

如果目標本身是泛型類型,則屬性將為空,為此您需要從 DeserializationContext 獲取 valueTtype:

@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
    if (property == null) { //  context is generic
        JMapToListParser parser = new JMapToListParser();
        parser.valueType = ctxt.getContextualType().containedType(0);
        return parser;
    } else {  //  property is generic
        JavaType wrapperType = property.getType();
        JavaType valueType = wrapperType.containedType(0);
        JMapToListParser parser = new JMapToListParser();
        parser.valueType = valueType;
        return parser;
    }
}

這是您如何訪問/解析自定義 Jackson Deserializer 的 {targetClass}。 當然,您需要為此實現 ContextualDeserializer 接口。

public class WPCustomEntityDeserializer extends JsonDeserializer<Object> 
              implements ContextualDeserializer {

    private Class<?> targetClass;

    @Override
    public Object deserialize(JsonParser jp, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {

        ObjectCodec oc = jp.getCodec();
        JsonNode node = oc.readTree(jp);

        //Your code here to customize deserialization
        // You can access {target class} as targetClass (defined class field here)
        //This should build some {deserializedClasObject}

        return deserializedClasObject;

    }   

    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property){
        //Find here the targetClass to be deserialized  
        String targetClassName=ctxt.getContextualType().toCanonical();
        try {
            targetClass = Class.forName(targetClassName);
        } catch (ClassNotFoundException e) {            
            e.printStackTrace();
        }
        return this;
    }
}

對於我的用例,上述解決方案均無效,因此我必須編寫自定義模塊。 你可以在 GitHub 上找到我的實現。

我想編寫一個自動從列表中刪除空白字符串的反序列化器。

暫無
暫無

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

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