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()从而获得泛型参数类型。在得到泛型参数类型之后,通过将泛型参数类型转化为合适的类型以方便后续进行处理。

0 0
原创粉丝点击