泛型
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 的类型转化接口使用方法(几个常用的方法):
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);
}
最长接受的参数是:Class、JavaType、TypeReference。
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 的相关类的信息和其他和序列化相关的信息的类。
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
如果入参的是 type,但是实际上是 class 的话,那么需要构建 JavaType:
Object obj = JacksonConstant.OM.readValue(json, JacksonConstant.OM.constructType(type));
if (obj instanceof Staff) {
return (Staff) obj;
}
如果入参为 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;
}