Gson是如何记录泛型信息的
来源:互联网 发布:现货软件哪个好 编辑:程序博客网 时间:2024/04/30 04:41
Gson是用来处理json格式文本数据的工具,提供fromJson()和toJson()的方法来实现反序列化和序列化。在反序列化的方法中,Gson提供fromJson(String json, Type typeOfT)重载方法来处理泛型。简单介绍下其使用方法,假设有如下json格式的数据A和B,A中s的属性是一个对象,B中s的属性是一个数据或集合,那么在定义反序列化的目标类型时,可以定义为如Result的类型。
json格式的数据:
String jsonDataA = "{\"succ\":{\"message\":\"success\"}}";String jsonDataB = "{\"succ\":[{\"message\":\"success\"}]}";
反序列化的目标类型Result:
public class Result<T> { T succ; public T getSucc() { return succ; } public void setSucc(T succ) { this.succ = succ; }}
Gson使用:
// jsonDataA的解析Result<Success> result = new Gson().fromJson(jsonDataA, new TypeToken<Result<Success>>(){}.getType());// jsonDataB的解析Result<List<Success>> result = new Gson().fromJson(jsonDataA, new TypeToken<Result<List<Success>>>(){}.getType());
通过以上例子可以看到,Gson对泛型的支持使得开发人员只需要定义一个泛型类型的Result对象即可满足上述不同格式下的json数据解析。
但是我们都知道,java中的泛型类型是运行期擦除的,需要通过合适的方法来处理泛型信息。本文就讲讲gson是如何记录泛型信息的。
TypeToken
Gson提供TypeToken类在编译期处理泛型信息。TypeToken的构造函数是protected修饰的,可通过如下方法定义一个泛型信息为List<String>
的typeToken对象。
TypeToken<List<String>> list = new TypeToken<List<String>>() {};
在TypeToken中,用以下3个变量用来保存泛型信息。
final Class<? super T> rawType; final Type type; final int hashCode;
在执行构造函数的时候,对以上3个变量进行赋值。
protected TypeToken() { this.type = getSuperclassTypeParameter(getClass()); this.rawType = (Class<? super T>) $Gson$Types.getRawType(type); this.hashCode = type.hashCode(); }
1. type
type变量的值是由getSuperclassTypeParameter(getClass())语句获得的。
/** * Returns the type from super class's type parameter in {@link $Gson$Types#canonicalize * canonical form}. */ static Type getSuperclassTypeParameter(Class<?> subclass) { // 获得带有泛型信息的父类 Type superclass = subclass.getGenericSuperclass(); // 如果没有泛型信息,则抛出异常 if (superclass instanceof Class) { throw new RuntimeException("Missing type parameter."); } ParameterizedType parameterized = (ParameterizedType) superclass; // 通过$Gson$Types.canonicalize把泛型信息封装为合适的类型 return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]); }
在getSuperclassTypeParameter()函数中,会尝试获取带有泛型信息的父类型。如果获取到,就通过ParameterizedType.getActualTypeArguments()获得泛型的实际类型。其返回结果是一个数组,按泛型参数的顺序排列。此处TypeToken只有一个泛型参数,故取索引位置为0的值。
获得泛型的实际类型之后,接着调用$Gson$Types.canonicalize()方法来标准化泛型的类型。
/** * Returns a type that is functionally equal but not necessarily equal * according to {@link Object#equals(Object) Object.equals()}. The returned * type is {@link java.io.Serializable}. */ public static Type canonicalize(Type type) { //普通的Class类型 if (type instanceof Class) { Class<?> c = (Class<?>) type; // 如果是数组类型,则包装成GenericArrayTypeImpl对象,并对数组的元素类型调用canonicalize递归进行处理 // 如果是普通的Class类型,则直接返回 return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c; } // 如果是泛型类型 else if (type instanceof ParameterizedType) { ParameterizedType p = (ParameterizedType) type; // 则包装成ParameterizedTypeImpl类型 return new ParameterizedTypeImpl(p.getOwnerType(), p.getRawType(), p.getActualTypeArguments()); } // 如果泛型参数是数组类型 else if (type instanceof GenericArrayType) { GenericArrayType g = (GenericArrayType) type; // 包装成GenericArrayTypeImpl,主要是进一步处理数组的元素类型 return new GenericArrayTypeImpl(g.getGenericComponentType()); } // 如果泛型参数是通配符 else if (type instanceof WildcardType) { WildcardType w = (WildcardType) type; return new WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds()); } else { // type is either serializable as-is or unsupported return type; } }
在canonicalize()方法中,对可能的Type类型进行枚举处理。对于泛型参数,可能的类型有Class、ParameterizedType、GenericArrayType、WildcardType,其他不识别的类型则默认返回。
在处理Class的时候,要对Array和普通类型分别处理。如果是数组类型,则要对数组元素进一步处理,比如List<String>[]
形式的数组,则要对List<String>
进一步包装处理。
在处理ParameterizedType的时候,比如List<String>
,则要进一步处理泛型类型中的泛型参数。观察Gson中自定义实现的ParameterizedTypeImpl类型。
public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) { // require an owner type if the raw type needs it if (rawType instanceof Class<?>) { Class<?> rawTypeAsClass = (Class<?>) rawType; checkArgument(ownerType != null || rawTypeAsClass.getEnclosingClass() == null); checkArgument(ownerType == null || rawTypeAsClass.getEnclosingClass() != null); } // 对ownerType调用canonicalize()方法进一步处理 this.ownerType = ownerType == null ? null : canonicalize(ownerType); // 对rawType调用canonicalize()方法进一步处理 this.rawType = canonicalize(rawType); this.typeArguments = typeArguments.clone(); for (int t = 0; t < this.typeArguments.length; t++) { checkNotNull(this.typeArguments[t]); checkNotPrimitive(this.typeArguments[t]); // 对泛型参数的实际类型调用canonicalize()方法进一步处理 this.typeArguments[t] = canonicalize(this.typeArguments[t]); } }
通过自定义实现的ParameterizedTypeImpl对象,则将泛型参数转化为合适的对象方便后续程序进一步处理。
理解了ParameterizedTypeImpl的处理方法,GenericArrayTypeImpl、WildcardType的处理看上去就简单多了。
public GenericArrayTypeImpl(Type componentType) { // 进一步处理元素类型 this.componentType = canonicalize(componentType); }
public WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) { checkArgument(lowerBounds.length <= 1); checkArgument(upperBounds.length == 1); if (lowerBounds.length == 1) { checkNotNull(lowerBounds[0]); checkNotPrimitive(lowerBounds[0]); checkArgument(upperBounds[0] == Object.class); // 对通配符的下限进一步处理 this.lowerBound = canonicalize(lowerBounds[0]); this.upperBound = Object.class; } else { checkNotNull(upperBounds[0]); checkNotPrimitive(upperBounds[0]); this.lowerBound = null; // 对通配符的上限进一步处理 this.upperBound = canonicalize(upperBounds[0]); } }
以上便完成了TypeToken中type变量的赋值过程。可以看到type类型是经过转化后的自定义的Type实现类。
2. rawType
rawType类型是通过以下语句进行赋值的,依赖于上文得到的type类型。
this.rawType = (Class<? super T>) $Gson$Types.getRawType(type);
对getRawType()函数进一步查看。
public static Class<?> getRawType(Type type) { // 如果是基本Class类型,则直接返回 if (type instanceof Class<?>) { // type is a normal class. return (Class<?>) type; } // 如果是泛型,则返回未指明泛型参数的类型 else if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; // I'm not exactly sure why getRawType() returns Type instead of Class. // Neal isn't either but suspects some pathological case related // to nested classes exists. Type rawType = parameterizedType.getRawType(); checkArgument(rawType instanceof Class); return (Class<?>) rawType; } // 对于泛型数组,则递归处理元素类型直至返回最终的类型。 // 比如List<String>[],则返回的是List[].class else if (type instanceof GenericArrayType) { Type componentType = ((GenericArrayType)type).getGenericComponentType(); return Array.newInstance(getRawType(componentType), 0).getClass(); } // 如果是泛型的形参,则返回Object.class else if (type instanceof TypeVariable) { // we could use the variable's bounds, but that won't work if there are multiple. // having a raw type that's more general than necessary is okay return Object.class; } // 如果是通配符,则返回通配符的上界 else if (type instanceof WildcardType) { return getRawType(((WildcardType) type).getUpperBounds()[0]); } else { String className = type == null ? "null" : type.getClass().getName(); throw new IllegalArgumentException("Expected a Class, ParameterizedType, or " + "GenericArrayType, but <" + type + "> is of type " + className); } }
至此,便赋值rawType类型。
3.hashCode
对于hashCode,其赋值是type的hashCode()值。
this.hashCode = type.hashCode();
总结:
在工具类实现反序列化的过程中,由于泛型有运行期擦除的特性,因此如何通过特定的方式记录下泛型信息是一个比较困难的问题。Gson通过让开发者匿名实现TypeToken类,并传递泛型参数类型,然后通过Class.getGenericSuperclass()从而获得泛型参数类型。在得到泛型参数类型之后,通过将泛型参数类型转化为合适的类型以方便后续进行处理。
- Gson是如何记录泛型信息的
- Gson是如何在运行时处理字段的泛型信息
- oracle如何记录用户的登陆信息
- 如何记录每天查询的信息
- 如何记录异常的 堆栈信息
- Google是如何搜集互联网信息的
- JSP是如何传到session信息的
- 信息图一般是如何绘制的?
- 4.2.3. 消息是如何记录的
- 网站数据是如何记录的
- 如何是弹出的信息提示框中的信息换行
- Map转json是如何保证顺序以及Gson的单例化
- gson和bson4jackson 的简单记录
- javaeye的访问人是如何记录的
- gson处理泛型的问题
- Gson和泛型擦除的解决方法
- 关于Gson泛型解析的解决方案。
- 关于Gson解析泛型的
- 长时间不发博客就会被封?
- 三角形的判断
- POJ-3450 Corporate Identity(KMP/后缀数组)
- 学习Java分为几个阶段,分别是什么?
- NEUOJ Problem48
- Gson是如何记录泛型信息的
- 11g rac grid安装 网络通不过 INS-41112
- 位运算
- 整理系列-20161117-nfdump-nfsen_cacti_nagios(我只是大自然的搬运工)
- 一、设置textView的行间距
- 学前班-怎么看原理图之GPIO与门电路
- 关于canvas文字居中的问题,特别是上下居中,垂直居中
- hibernate查询返回不是bean的问题
- Android之时间戳的简单使用