Gson之TypeAdapter的工作原理分析(1)

来源:互联网 发布:枫叶知秋 编辑:程序博客网 时间:2024/05/22 04:45
  在Gson的有一个JsonParser类,该类是final类型,它的作用就将json串解析成JsonElement对象。提供了三个重载方法:分别可以解析String串、Reader输入流、JsonReader输入流的方式。  简单的应用如下测试代码:
    String json = "{ \"name\":\"java书籍\", \"authors\":[\"Jerry\",\"Tom\"]}";        JsonParser parser = new JsonParser();        JsonElement ele = parser.parse(json);        System.out.println(ele);        System.out.println(ele.getAsJsonObject().get("authors").isJsonArray());

追踪parser方法:辗转调用了Stream.parser(JsonReader)方法

 public static JsonElement parse(JsonReader reader) throws JsonParseException {      boolean isEmpty = true;      //这是核心方法,稍后会简单说明      reader.peek();      isEmpty = false;      return TypeAdapters.JSON_ELEMENT.read(reader);      if (isEmpty) {        return JsonNull.INSTANCE;      }  }

该方法最终执行TypeAdapter.JSON_ELEMENT.reader并返回组装好的JsonElement对象,那么就具体看看该方法都做了什么:

/****为TypeAdapter的静态嵌套类,实现了TypeAdapter**/public static final TypeAdapter<JsonElement> JSON_ELEMENT = new TypeAdapter<JsonElement>() {      /****      *该方法的主要作用就是让json串封装成你希望的对象,在这里是把json串      */    @Override public JsonElement read(JsonReader in) throws IOException {   //调用peek()方法对json传进行读取和解析,并返回对应的JsonToke类型      switch (in.peek()) {      case STRING://如果是字符类型,封装并返回之        return new JsonPrimitive(in.nextString());      case NUMBER://数字类型,例如double,long,int        String number = in.nextString();        return new JsonPrimitive(new LazilyParsedNumber(number));      case BOOLEAN://boolean 类型:true or false        return new JsonPrimitive(in.nextBoolean());      case NULL://null        in.nextNull();        return JsonNull.INSTANCE;      case BEGIN_ARRAY://数组类型         //解析此数组,并添加到array中去        JsonArray array = new JsonArray();        in.beginArray();  //循环遍历数组,并调用read方法解析成JsonElement对象,添加到array中去        while (in.hasNext()) {          array.add(read(in));        }        in.endArray();        return array;//返回之      case BEGIN_OBJECT://如果只json对象,例如{"name":"value"}        JsonObject object = new JsonObject();        in.beginObject();        //解析该json对象,并获取name 和value放入object中        //注意value可能是JsonElement,所以要调用read(in)        while (in.hasNext()) {          object.add(in.nextName(), read(in));        }        in.endObject();        return object;      case END_DOCUMENT:      case NAME:      case END_OBJECT:      case END_ARRAY:      default:        throw new IllegalArgumentException();      }    }    }

上面的方法主要是递归调用了read方法讲json串封装成JsonElement对象供我们使用,关于JsonElement的简单说明,见此博客
同时此方法也能体现出JsonElement是怎么在json的解析过程中工作的。
上面使用的JSON_ELEMENT只是TypeAdapter的一个实现类,下面让我们看看TypeAdapter这个抽象类都能做些什么。
简而言之:该类的作用就是把json串封装成你指定的Java对象!
该类的API对它的用法说明很清楚,简单的应用模板如下:

Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class,     new TypeAdapter<Foo>() {        public Foo read(JsonReader in) throws IOException {         if (in.peek() == JsonToken.NULL) {//进行非空判断           in.nextNull();            return null;          }          //读取json串并封装成Foo对象返回之        }        /***        * 该方法在gson.toJons(Obj)的时候会调用        **/        public void write(JsonWriter out, Foo src) throws IOException {          if (src == null) {//进行非空判断           out.nullValue();           return;          }          //把Food对象制定成你自己定义的格式的字符串进行输出:不一定是json格式了,就看你怎么组织        }      }).create();

上面的使用模板都需要进行非空判断,当然如果你觉得上面的方法每次写的时候都要进行非空判断麻烦的话,也可以不写,TypeAdapter为此提供了一个优雅的解决方法:

/****匿名内部类的完美应用**/ public final TypeAdapter<T> nullSafe() {    return new TypeAdapter<T>() {      @Override public void write(JsonWriter out, T value) throws IOException {        if (value == null) {          out.nullValue();        } else {          TypeAdapter.this.write(out, value);        }      }      @Override public T read(JsonReader reader) throws IOException {        if (reader.peek() == JsonToken.NULL) {          reader.nextNull();          return null;        }        return TypeAdapter.this.read(reader);      }    };  }

所以上面的使用方式就变成:

 Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class,      new TypeAdapter<Foo>() {        public Foo read(JsonReader in) throws IOException {          // read a Foo from in and return it       }        public void write(JsonWriter out, Foo src) throws IOException {          // write src as JSON to out        }      }.nullSafe()).create();

其实,在我们调用gson的fromJson(String,class)方法的时候,沿着方法调用的路径::fromJson(String,Class)–>fromJson(String,Type) (Type包含了class的报名和类名 class package.className)–>fromJson(Reader,Type)–>fromJson(JsonReader,Type);最终调用了fromJson(Jsonreader,Type):

 public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {      reader.peek();      isEmpty = false;      TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);      TypeAdapter<T> typeAdapter = getAdapter(typeToken);      //此处正是你自定义的Adapter方法或者Gson自带的Adapter的用武之地。      T object = typeAdapter.read(reader);      return object;  }

当然自定义的Adapter是怎么让Gson使用的,需要调用registerTypeAdapter方法,把你自定义的Adapter注册进去:

public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) {    //此处有省略代码    if (typeAdapter instanceof TypeAdapter<?>) {      //至于TypeToken后面会另起篇幅进行说明      //把你的Adapter和传入的type重新封装成TypeAdapterFactory并加入到factories中去。      factories.add(TypeAdapters.newFactory(TypeToken.get(type), (TypeAdapter)typeAdapter));    }    return this;  }

正如证明代码所示,先把自定义的Adapter和Type封装成TypeAdapterFactory对象,然后添加到factories这个List集合中去,对于TypeAdapterFactory的说明,会另起篇幅进行说明。然后调用GsonBuilder.create()方法创建Gson对象:

public Gson create() {    List<TypeAdapterFactory> factories = new ArrayList<TypeAdapterFactory>();    //把GsonBuilder的factories 传入到新定义的factories 中并交给Gson处理。    factories.addAll(this.factories);    Collections.reverse(factories);    factories.addAll(this.hierarchyFactories);    addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, factories);    return new Gson(excluder, fieldNamingPolicy, instanceCreators,        serializeNulls, complexMapKeySerialization,        generateNonExecutableJson, escapeHtmlChars, prettyPrinting,        serializeSpecialFloatingPointValues, longSerializationPolicy, factories);  }

从上面的fromJson(JsonReader,type)方法中,先调用getAdapter(typeToken)方法获取到Adapter,当然也包含你自定义的Adapter:

  private final Map<TypeToken<?>, TypeAdapter<?>> typeTokenCache      = Collections.synchronizedMap(new HashMap<TypeToken<?>, TypeAdapter<?>>()); private final ThreadLocal<Map<TypeToken<?>, FutureTypeAdapter<?>>> calls      = new ThreadLocal<Map<TypeToken<?>, FutureTypeAdapter<?>>>(); public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {    //从typeTokenCache获取对应的TypeAdapter    TypeAdapter<?> cached = typeTokenCache.get(type);    if (cached != null) {      return (TypeAdapter<T>) cached;    }    Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get();    boolean requiresThreadLocalCleanup = false;    if (threadCalls == null) {      threadCalls = new HashMap<TypeToken<?>, FutureTypeAdapter<?>>();      calls.set(threadCalls);      requiresThreadLocalCleanup = true;    }    //    FutureTypeAdapter<T> ongoingCall = (FutureTypeAdapter<T>) threadCalls.get(type);    if (ongoingCall != null) {      return ongoingCall;    }    try {      FutureTypeAdapter<T> call = new FutureTypeAdapter<T>();      threadCalls.put(type, call);      //遍历Gson的factories集合,其中你的Adapter封装后的TypeAdapterFacotry也在里面。      for (TypeAdapterFactory factory : factories) {        //重新从你封装过后的TypeAdapterFacotry获取到之前封装过后的Adapter        TypeAdapter<T> candidate = factory.create(this, type);        if (candidate != null) {          //设置代理,这个在之后的博客说明          call.setDelegate(candidate);          //放入缓存中          typeTokenCache.put(type, candidate);          return candidate;        }      }      throw new IllegalArgumentException("GSON cannot handle " + type);    } finally {      threadCalls.remove(type);      if (requiresThreadLocalCleanup) {        calls.remove();      }    }  }

到此为止TypeAdapter的工作方式已经说明了,现在总结一下:
1)通过GsonBuilder注册TypeAdapter,并把TypeAdapter封装成TypeAdpterFactory对象
2)将封装成的TypeAdapterFactory通过GsonBuilder的create传入Gson对象中并返回
3)调用gson.fromJson方法,调用getTypeAdapter方法返回你自定义的Adapter,并调用其reader方法进行处理!

最后盗用两个图说明Adapter的工作原理(此为盗图链接):
Java对象转成json串
Adapte的工作方式

可参考资料:

1 0
原创粉丝点击