Jackson序列化,反序列化,和泛型

Jackson和泛型

在序列化和反序列化的過程中,泛型是永遠離不開的主題,那麼泛型有哪幾種呢?Jackson又是如何來處理這些問題的呢?

泛型的類型參考

index Name Example
1 ParameterizedType 參數化類型,即泛型;例如:List、Map<K,V>等帶有參數化的對象,自定義的如Box也是
2 TypeVariable 類型變量,即泛型中的變量;例如:T、K、V等變量,可以表示任何類;在這需要強調的是,TypeVariable代表着泛型中的變量,而ParameterizedType則代表整個泛型
3 GenericArrayType 泛型數組類型,用來描述ParameterizedType、TypeVariable類型的數組;即List[] 、T[]等
4 Class Class是Type的一個實現類,屬於原始類型,是Java反射的基礎,對Java類的抽象
5 WildcardType 泛型表達式(或者通配符表達式),即? extend Number、? super Integer這樣的表達式;WildcardType雖然是Type的子接口,但卻不是Java類型中的一種

那麼Jackson 是如何處理的呢?

Jackson的類型轉化接口使用方法(幾個常用的方法)

 public <T> T readValue(JsonParser p, Class<T> valueType)
        throws IOException, JsonParseException, JsonMappingException
    {
        return (T) _readValue(getDeserializationConfig(), p, _typeFactory.constructType(valueType));
    } 

    @Override
    @SuppressWarnings("unchecked")
    public <T> T readValue(JsonParser p, TypeReference<?> valueTypeRef)
        throws IOException, JsonParseException, JsonMappingException
    {
        return (T) _readValue(getDeserializationConfig(), p, _typeFactory.constructType(valueTypeRef));
    }

    @Override
    @SuppressWarnings("unchecked")
    public final <T> T readValue(JsonParser p, ResolvedType valueType)
        throws IOException, JsonParseException, JsonMappingException
    {
        return (T) _readValue(getDeserializationConfig(), p, (JavaType) valueType);
    }
    
    public <T> T readValue(JsonParser p, JavaType valueType)
        throws IOException, JsonParseException, JsonMappingException
    {
        return (T) _readValue(getDeserializationConfig(), p, valueType);
    }

最長接受的參數是:

  1. Class
  2. JavaType
  3. TypeReference

class無需多說明,那麼什麼是JavaType,TypeReference呢?

  1. JavaType:

在Jackson中可能是最終的類型吧,TypeReference最終還是會轉化爲JavaType,那麼什麼是JavaType呢?

Base class for type token classes used both to contain information and as keys for deserializers.
Instances can (only) be constructed by
com.fasterxml.jackson.databind.type.TypeFactory.
Since 2.2 this implements {@link java.lang.reflect.Type} to allow
it to be pushed through interfaces that only expose that type.

用於包含信息和作爲反序列化器的鍵的類型標記類的基類。只能通過TypeFactory來實例化。

通常的使用或者構造方式是:

1. 通過objectMapper.construct
JavaType javaType = JacksonConstant.OM.constructType(type);
2. 通過TypeProvier
JavaType javaType1 = TypeFactory.defaultInstance().constructType(type);

其實方式1本質上是2,源代碼如下:

public JavaType constructType(Type t) {
    //本質還是通過TypeFactory來實現的
    return _typeFactory.constructType(t);
}

那麼TypeFactory是如何實例化入參的呢?因爲在反序列化的過程中,我們的入參是Type,但正如我們上面所述的,Type類包含了五個子類, Class, ParameterizedType, TypeVariable,WildCard,GenericArrayType,查看源碼:

protected JavaType _fromAny(ClassStack context, Type type, TypeBindings bindings)
    {
        JavaType resultType;

        // simple class?
        if (type instanceof Class<?>) {
            // Important: remove possible bindings since this is type-erased thingy
            resultType = _fromClass(context, (Class<?>) type, EMPTY_BINDINGS);
        }
        // But if not, need to start resolving.
        else if (type instanceof ParameterizedType) {
            resultType = _fromParamType(context, (ParameterizedType) type, bindings);
        }
        else if (type instanceof JavaType) { // [databind#116]
            // no need to modify further if we already had JavaType
            return (JavaType) type;
        }
        else if (type instanceof GenericArrayType) {
            resultType = _fromArrayType(context, (GenericArrayType) type, bindings);
        }
        else if (type instanceof TypeVariable<?>) {
            resultType = _fromVariable(context, (TypeVariable<?>) type, bindings);
        }
        else if (type instanceof WildcardType) {
            resultType = _fromWildcard(context, (WildcardType) type, bindings);
        } else {
            // sanity check
            throw new IllegalArgumentException("Unrecognized Type: "+((type == null) ? "[null]" : type.toString()));
        }
        // 21-Feb-2016, nateB/tatu: as per [databind#1129] (applied for 2.7.2),
        //   we do need to let all kinds of types to be refined, esp. for Scala module.
        if (_modifiers != null) {
            TypeBindings b = resultType.getBindings();
            if (b == null) {
                b = EMPTY_BINDINGS;
            }
            for (TypeModifier mod : _modifiers) {
                JavaType t = mod.modifyType(resultType, type, b, this);
                if (t == null) {
                    throw new IllegalStateException(String.format(
                            "TypeModifier %s (of type %s) return null for type %s",
                            mod, mod.getClass().getName(), resultType));
                }
                resultType = t;
            }
        }
        return resultType;
    }

Jackson本身會根據類型來生成JavaType,記錄相關的信息。

總結來說:JavaType,Jackson 自定義的一個記錄入參Type的相關類的信息和其他和序列化相關的信息的類。

  1. TypeReference:

源碼如下:

public abstract class TypeReference<T> implements Comparable<TypeReference<T>>
{
    protected final Type _type;
    
    protected TypeReference()
    {
        Type superClass = getClass().getGenericSuperclass();
        if (superClass instanceof Class<?>) { // sanity check, should never happen
            throw new IllegalArgumentException("Internal error: TypeReference constructed without actual type information");
        }
        _type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
    }

    public Type getType() { return _type; }
}

通用的使用方式是:

  Map<String, Staff> json2Map = JacksonConstant.OM.readValue(staffMapJson, new TypeReference<Map<String, Staff>>() {
        });

構建一個內部匿名類,名字是運行類下的$number,繼承了TypeReference<Map<String,Staff>>,保存了最原始的數據類型,通過

getClass().getGenericSuperclass()

獲取parameterizedType,類型爲TypeReference,通過parameterizedType.getActualTypeArguments()[0],獲取最終的類型:Map<String, Staff>,
這樣的話就保留了需要的類型。

使用

入參爲class
Staff staff1 = mapper.readValue(jsonInString, Staff.class);
//Pretty print
String prettyStaff1 = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(staff1);
入參爲type
  1. 如果入參的是type,但是實際上是class的話,那麼需要構建JavaType
 Object obj = JacksonConstant.OM.readValue(json, JacksonConstant.OM.constructType(type));
if (obj instanceof Staff) {
    return (Staff) obj;
}
  1. 如果入參爲ParameterizedType的話,如果Map<K,V>,那麼需要轉化爲TypeReference,代碼case如下:
Map<String, Staff> json2Map = JacksonConstant.OM.readValue(staffMapJson, new TypeReference<Map<String, Staff>>() {
});

同理可得,對於List的反序列化可以推斷爲如下:

public static List<Staff> json2List() throws IOException {
    String json = "[{\"name\":\"rb.x\",\"age\":1,\"position\":\"sh\",\"salary\":100.23,\"skills\":[\"java\",\"mysql\"]}]";
    //在反序列化爲List的過程中,list<T> 和Map<K,V>本質上是parameterizedType
    List<Staff> staffList = JacksonConstant.OM.readValue(json, new TypeReference<List<Staff>>() {
    });
    System.out.println(staffList.size());
    return staffList;
}

可選:Jackson本身給了非常多的構造方法來滿足不同的需求,

JacksonConstant.OM.getTypeFactory().constructXXXX()系列

具體可以看看源碼

這裏以list爲例子:

public static List<Staff> json2List2() throws IOException {
    String json = "[{\"name\":\"rb.x\",\"age\":1,\"position\":\"sh\",\"salary\":100.23,\"skills\":[\"java\",\"mysql\"]}]";
    //在反序列化爲List的過程中,list<T> 和Map<K,V>本質上是parameterizedType
    List<Staff> staffList = JacksonConstant.OM.readValue(json, JacksonConstant.OM.getTypeFactory().constructCollectionType(List.class, Staff.class));
    System.out.println(staffList.size());
    return staffList;
    }

constructCollectionType()實際返回的是JavaType的一個子類。

  1. 如果入參是GenericArrayType(當然通常來說是不推薦這種寫法的),case如下:
public static Map<String, Staff>[] json2MapList() throws IOException {
    String mapArrayJson = "[{\"id_1\":{\"name\":\"rb.x\",\"age\":1,\"position\":\"sh\",\"salary\":100.23,\"skills\":[\"java\",\"mysql\"]}}]";
    Map<String, Staff>[] mapArray = JacksonConstant.OM.readValue(mapArrayJson, JacksonConstant.OM.getTypeFactory()
            .constructArrayType(JacksonConstant.OM.getTypeFactory().constructMapType(Map.class, String.class, Staff.class)));
    System.out.println(mapArray[0].get("id_1").getName());
    return mapArray;
}

其他如TypeVariable和wildCard的話就不舉例了,因爲遇到的太少了,如果遇到了再總結吧。
參考代碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章