JPJson基本介绍

项目的Github地址:https://github.com/androidfans/JPJson
最近基于JParsec开发了JPJson这样一个Json解析库.对外的接口模仿了Google的Gson,用户只需要定义一个JavaBean,然后调用JPJson的fromJson()方法,传入一段json字符串以及一个Class对象即可.fromJson会返回你传入的Class对象的一个实例.
下面是代码示例:

//首先是JavaBean的定义
class CHS{
    //权限修饰符可以任意设置
    //使用泛型指定list里面所存放的具体类型
    private List<CH> ch;
}
class CH{
    public String names;
    public List<Integer> datas;
}

//函数调用的代码
String json = "{\"ch\":{\"names\":\"怡美家园\",\"datas\":[1,2,3,4,5,6,7,8]}}"
JPJson jpJson = new JPJson();
CHS chs = jpJson.fromJson(json,CHS.class);
//这样就可以获得一个被解析好的CHS对象啦

如何在运行时获取泛型的类型

学过Java的人都知道,Java的泛型是编译时实现的,运行时泛型信息是被擦除的.但是我们为了解析Json数组就必须知道应该为给定的Class的List类型解析出什么样类型的对象以赋值.这个问题看上去是不可解决的了.但是因为我的接口设计是仿照Gson的,而Gson是可以实现这个功能的.带着疑问,我在Gson的代码中找到了这个.

private static final class ParameterizedTypeImpl implements ParameterizedType, Serializable {
    private final Type ownerType;
    private final Type rawType;
    private final Type[] typeArguments;

    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;
        boolean isStaticOrTopLevelClass = Modifier.isStatic(rawTypeAsClass.getModifiers())
            || rawTypeAsClass.getEnclosingClass() == null;
        checkArgument(ownerType != null || isStaticOrTopLevelClass);
      }

      this.ownerType = ownerType == null ? null : canonicalize(ownerType);
      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]);
        this.typeArguments[t] = canonicalize(this.typeArguments[t]);
      }
    }

事实上ParameterizedType就是我们需要寻找的带泛型的类啦.但是上面这段代码由于是在Gson的类型系统设计下,所以显得不是那么好懂.我搜索了其中的关键字后,在JPJson中实现了下面的代码.

Class fieldType = field.getType();
if (fieldType.isAssignableFrom(List.class)) {
    ParameterizedType pt = null;
    try {
        pt = (ParameterizedType) field.getGenericType();
    } catch (ClassCastException e) {
        throw new RuntimeException("you must offer a genericType on the bean's list type for json array to parse");
    }
    Class cla = (Class) pt.getActualTypeArguments()[0];
    fieldType = cla;
                    }

上面这段代码中

  • 首先我使用field.getType()获取了给定Class参数的类型.
  • 然后使用fieldType.isAssignableFrom(List.class)判断该field是否是一个List.
  • 如果是List类型,下面关键的来了,我使用field.getGenericType()获取带泛型的List类型
  • 然后将其强转为ParameterizedType类型.
  • 这里要捕获一个类型强转的异常,因为如果对应的List没有填写泛型类型的话,那么这次的强转会出异常.这个异常是由于用户提供的泛型类没有带泛型类型导致的,而我们确实需要这个类型.所以我们重新抛出一个新的吟唱并且带上必要的提示信息.
  • ParameterizedType是包含了泛型的Field类型所以实际上它应该是java.util.List<java.lang.String>这种形式的类型.所以我们使用Class cla = (Class) pt.getActualTypeArguments()[0]这个方法来获取真正的泛型类型.

下次各位读者需要获取泛型的类型的话,就可以使用这种方法哦.

运行时获取泛型的原理

这里摘抄一段来自书上的话,这是我之前看这本书是看到过的,书上只是提供了原理,但是没说在代码中应该怎么使用,今天使用过后,算是对原理有了更深的理解.

由于泛型的引入,各种场景(虚拟机解析,反射等)下的方法调用都可能会对原有的基础产生影响和新的需求,如在泛型类中如何获取传入的参数化类型等.因此JCP组织对虚拟机规范做出了相应的修改,引入了诸如Signature,LocalVariableTypeTable等新的属性用于解决伴随泛型而来的参数类型识别问题,Signature是其中最重要的一项属性,它的作用就是存储一个方法在字节码层面的特征签名,这个属性中保存的参数类型不是原生类型而是包括了参数化类型的信息.修改后的虚拟机规范要求所有能识别49.0以上版本的Class文件虚拟机都要能正确识别Signature参数.