Gson框架学习

2017-04-26  本文已影响152人  Nj_第一批老去的90后

http://www.jianshu.com/p/89c314ae8c0b

介绍

Gson(又称Google Gson)是Google公司发布的一个开放源代码的Java库,主要用途为序列化Java对象为JSON字符串,或反序列化JSON字符串成Java对象。

基本概念

JsonPrimitive —— 例如一个字符串或整型
JsonObject—— 一个以 JsonElement 名字(类型为 String)作为索引的集合。也就是说可以把 JsonObject 看作值为 JsonElement 的键值对集合。
JsonArray—— JsonElement 的集合。注意数组的元素可以是四种类型中的任意一种,或者混合类型都支持。
JsonNull—— 值为null

gson解决的问题

gson注解

源码分析

Gson其实就是一套模型转换工具,他的一端连接的是json数据格式编码流,另一头就是我们的Java对象模型。我们先给自己找一个入口点,看下Gson一个常见的例子:

public static class ClassRoom {
      public String roomName;
      public int number;

      public String toString() {
         return "[" + roomName + ":" + number + "]";
      }
 }

 public static class User {
      public String name;
      public int age;
      private ClassRoom room;

      @Override
      public String toString() {
       // TODO Auto-generated method stub
       return name + "->" + age + ":" + room;
      }
 }

我们尝试传入一个json格式的数据串

Gson gson = new Gson();
String strJson = "{name:'david',age:19,room:{roomName:'small',number:1}}";
User u = gson.fromJson(strJson, User.class);

在上述代码中,gson相当于起到了一个数据适配器的作用,将一个Json的数据格式转换为一个Java业务可用的数据模型。而这一层的转换,是通过Gson的fromJson方法实现的。

public <T> T fromJson(String json, Type typeOfT) throws JsonSyntaxException {
    if (json == null) {
      return null;
    }
    StringReader reader = new StringReader(json);
    T target = (T) fromJson(reader, typeOfT);
    return target;
  }

Gson支持以流的方式来读取字符,为了接口的复用性,如果你的第一个参数输入的是一个String对象,Gson一样会将它包装成为一个流对象StringReader。Gson的fromJson会调用到统一的接口方法 T fromJson(JsonReader reader, Type typeOfT) 中去:

//code 2
 public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
        boolean isEmpty = true;
        boolean oldLenient = reader.isLenient();
        reader.setLenient(true);//不严格按照Json标准语法
        try {
            reader.peek();
            isEmpty = false;
            TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);
            TypeAdapter<T> typeAdapter = getAdapter(typeToken);
            T object = typeAdapter.read(reader);
            return object;
        } catch (EOFException e) {
               ............
        }
    }

在code2代码主要做了下面的事情:

  1. 我们传入的一个StringReader,将会再次被包装成为一个Json语法格式的JsonReader对象;
  2. 通过调用setLenient方法,给Json语法提供更加大的宽容度;
  3. 通过reader.peek方法,先做一次语法检查,并获取第一个非空字符,通过这个非空字符来标记下一个解析格式;
  4. 通过传入的类型Type,来获取这个类型对应的TypeToken;
  5. 通过生成的TypeToken来生成对应的适配器;(Gson的作用,是将Json数据模型往不同平台上的Java对象转换,实际上可以算是一种平台接口的转换,这里,Gson用了适配器的方式来实现这套方案)。
    我们来一步步地看下代码,代码一进来,reader就调用了一个peek操作,这是为什么呢?我们知道,在流式对象中,peek的目的是为了查看下一个字符,JsonReader也自然如此。JsonReader通过一次peek先做一个简单的语法校验,然后标注当前JsonReader解析的时候将以何种形式解析。
//code3
public JsonToken peek() throws IOException {
    int p = peeked;
    if (p == PEEKED_NONE) {
      p = doPeek();
    }
   ....
}

peek方法并不像我们之前的流式文件那样,返回一个真实的数据,而是返回一个当前的解析状态标识JsonToken。JsonToken是一个枚举类型,用来标识在Json数据格式中的各种语法单位。而在peek()函数的内部,又调用了一个doPeek()。虽然两个名字非常相同,但是返回的结果却是完全不同。doPeek是真正意义上的返回数据,也就是返回一个真实的字符:

//code doPeek()
 private int doPeek() throws IOException {
      stack[stackSize - 1] = JsonScope.NONEMPTY_DOCUMENT;
       switch (c) {
                case '"':
                    return peeked = PEEKED_DOUBLE_QUOTED_NAME;
                case '\'':
                    checkLenient();
                    return peeked = PEEKED_SINGLE_QUOTED_NAME;
                case '}':
                    if (peekStack != JsonScope.NONEMPTY_OBJECT) {
                        return peeked = PEEKED_END_OBJECT;
                    } else {
                        throw syntaxError("Expected name");
                    }
                default:
      }
  }

在doPeek()函数中,需要用到一个叫做stack变量的数据,里面存放的是叫做JsonScope的一堆常量。这是因为,Gson对Json的解析方式,是采用栈式的解析,按照层次解析的方式,这种层次解析的方式需要用栈来记录一些先前的状态。而这个stack的初始值,在一个初始代码块中设置:


 private int[] stack = new int[32];
  public int stackSize = 0;
  {
      stack[stackSize++] = JsonScope.EMPTY_DOCUMENT;
  }

可见,stack初始栈顶数据为EMPTY_DOCUMENT常量。这时候,我们回到doPeek函数中去。我们可以看到:

  1. if将调用line1中的方法体。将栈顶数据替换为NONEMPTY_DOCUMENT状态
  2. 在line2中获取下一个非空白字符,赋值给c变量
  3. 在line3中将执行case '{'将PEEKED_BEGIN_OBJECT 整型常量付给peeked变量,然后返回。
    然后我们返回我们的peek()方法:
int p = peeked;
    if (p == PEEKED_NONE) {
      p = doPeek();
    }
    switch (p) {
    case PEEKED_BEGIN_OBJECT:
      return JsonToken.BEGIN_OBJECT;
}

doPeek方法返回一个整型的常量后,peek方法又通过这个整型常量转化为对应的JsonToken变量。或许有些看官就会问了,如果是这样的话,JsonToken的意义何在呢?你完全可以用返回的peeked变量来代替JsonToken的数据?

case PEEKED_SINGLE_QUOTED_NAME:
    case PEEKED_DOUBLE_QUOTED_NAME:
    case PEEKED_UNQUOTED_NAME:
      return JsonToken.NAME;

我们可以看到,三种peek出来的数据类型,实际上可能对应一种Json中的标识符,那么,也就是说peek出来的符号类型(整型常量)跟Json中的标识符(JsonToken枚举常量),实际上是一种多对应的数据关系,所以不能混用。另外,我们在peek()方法中所生成的这个JsonToken,在Reader中并没有记录,也就是说,对于数据的解析,Reader还是关心doPeek方法中生成的中间状态数据。

我们回到code2中,根据上面的分析,我们已经知道reader.peek()方法实际上是初始了一些数据变量。接下去,fromJson方法将通过TypeToken.get的静态方法来生成一个TypeToken对象。

public static TypeToken<?> get(Type type) {
        return new TypeToken<Object>(type);
  }

TypeToken的get方式,是一种静态构造工厂,它将直接返回一个TypeToken对象。

Gson gson = new Gson();
String strJson = "{name:'david',age:19,room:{roomName:'small',number:1}}";
User u = gson.fromJson(strJson, User.class);

当TypeToken.get(User.class)的时候,返回的就是一个内部持有一个User.class为Type的TypeToken变量,在通过TypeToken.get(Type)方法调用以后,将会通过一个叫getAdapter的方法来获得typeAdapter对象。
TypeAdapter是Gson代码体系中的重中之重,它承担了真正意义上的转换工作。
Gson在Adapter的管理上,利用了享元 ,毕竟,解析类结构有一定的时间消耗,为了降低这种时间上的开销,Gson使用了缓冲池来管理这种映射关系。或许读者有一个问题:既然TypeToken.get方法每次都是利用静态工厂的方式构造一个新的TypeToken,那么你在放入cache中并不能起到cache的作用?
答案是:可以的。回答这个问题,我们需要回到TypeToken的源码:


    @Override
    public final int hashCode() {
        return this.hashCode;
    }

    @Override
    public final boolean equals(Object o) {
        return o instanceof TypeToken<?> && $Gson$Types.equals(type, ((TypeToken<?>) o).type);
    }

如上述代码显示,TypeToken利用相同的hashCode和复写的equals方法,来解决不同对象的冲突问题。好的,我们继续回到getAdapter方法:

// code Gson.getAdapter
public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
      TypeAdapter<?> cached = typeTokenCache.get(type == null ? NULL_KEY_SURROGATE : 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;
      }

      // the key and value type parameters always agree
      FutureTypeAdapter<T> ongoingCall = (FutureTypeAdapter<T>) threadCalls.get(type);
      if (ongoingCall != null) {
          return ongoingCall;
      }

      try {
          FutureTypeAdapter<T> call = new FutureTypeAdapter<T>();
          threadCalls.put(type, call);

          for (TypeAdapterFactory factory : factories) {
              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();
          }
      }
  }

当cache中并不存在需要的适配器的时候,并且该线程并不存在有缓冲池对象的时候,Gson将构造一个Map对象,放入线程中。同时我们还可以发现Gson的Cache的特点:

  1. 在Gson的Cache管理里,实际上应用了二级缓冲;
  2. Gson对象内部有一个缓冲,而Gson对象所在的线程又有一个共享Cache;
  3. 在不同的线程之间,又使用不同的cache。
//核心代码
FutureTypeAdapter<T> call = new FutureTypeAdapter<T>();
      threadCalls.put(type, call);
      for (TypeAdapterFactory factory : factories) {
        TypeAdapter<T> candidate = factory.create(this, type);
        if (candidate != null) {
          call.setDelegate(candidate);
          typeTokenCache.put(type, candidate);
          return candidate;
        }
      }

Adapter的构造是通过遍历一个factories集合类来完成的。做法也很简单,Factory通过匹配传入的TypeToken类型,然后是否匹配,如果匹配将生成对象,不匹配就返回null。同时,一旦生成Adapter对象,程序立马返回,说明构造对象是有优先级别的,可能你的AFactory通过规则可以生成对象,BFactory通过规则也可以生成对象,那么这个时候如果AFactory在BFactory的前面,那么将优先使用AFactory来构造Adapter。
factories的初始化数据在Gson的构造器里:

 Gson(final Excluder excluder, final FieldNamingStrategy fieldNamingStrategy,
         final Map<Type, InstanceCreator<?>> instanceCreators, boolean serializeNulls,
         boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe,
         boolean prettyPrinting, boolean lenient, boolean serializeSpecialFloatingPointValues,
         LongSerializationPolicy longSerializationPolicy,
         List<TypeAdapterFactory> typeAdapterFactories)
{
     factories.add(TypeAdapters.JSON_ELEMENT_FACTORY);//针对JsonElement类做特别处理
        factories.add(ObjectTypeAdapter.FACTORY);//针对Object类做特殊处理

        // the excluder must precede all adapters that handle user-defined types
        factories.add(excluder);//用于拦截一些不在使用的类型

        // user's type adapters
        factories.addAll(typeAdapterFactories);

        // type adapters for basic platform types
        factories.add(TypeAdapters.STRING_FACTORY);
        factories.add(TypeAdapters.INTEGER_FACTORY);
        factories.add(TypeAdapters.BOOLEAN_FACTORY);
        factories.add(TypeAdapters.BYTE_FACTORY);
        factories.add(TypeAdapters.SHORT_FACTORY);
        TypeAdapter<Number> longAdapter = longAdapter(longSerializationPolicy);
        factories.add(TypeAdapters.newFactory(long.class, Long.class, longAdapter));
        factories.add(TypeAdapters.newFactory(double.class, Double.class,
                doubleAdapter(serializeSpecialFloatingPointValues)));
        factories.add(TypeAdapters.newFactory(float.class, Float.class,
                floatAdapter(serializeSpecialFloatingPointValues)));
        factories.add(TypeAdapters.NUMBER_FACTORY);
        factories.add(TypeAdapters.ATOMIC_INTEGER_FACTORY);
        factories.add(TypeAdapters.ATOMIC_BOOLEAN_FACTORY);
        factories.add(TypeAdapters.newFactory(AtomicLong.class, atomicLongAdapter(longAdapter)));
        factories.add(TypeAdapters.newFactory(AtomicLongArray.class, atomicLongArrayAdapter(longAdapter)));
        factories.add(TypeAdapters.ATOMIC_INTEGER_ARRAY_FACTORY);
        factories.add(TypeAdapters.CHARACTER_FACTORY);
        factories.add(TypeAdapters.STRING_BUILDER_FACTORY);
        factories.add(TypeAdapters.STRING_BUFFER_FACTORY);
        factories.add(TypeAdapters.newFactory(BigDecimal.class, TypeAdapters.BIG_DECIMAL));
        factories.add(TypeAdapters.newFactory(BigInteger.class, TypeAdapters.BIG_INTEGER));
        factories.add(TypeAdapters.URL_FACTORY);
        factories.add(TypeAdapters.URI_FACTORY);
        factories.add(TypeAdapters.UUID_FACTORY);
        factories.add(TypeAdapters.CURRENCY_FACTORY);
        factories.add(TypeAdapters.LOCALE_FACTORY);
        factories.add(TypeAdapters.INET_ADDRESS_FACTORY);
        factories.add(TypeAdapters.BIT_SET_FACTORY);
        factories.add(DateTypeAdapter.FACTORY);
        factories.add(TypeAdapters.CALENDAR_FACTORY);
        factories.add(TimeTypeAdapter.FACTORY);
        factories.add(SqlDateTypeAdapter.FACTORY);
        factories.add(TypeAdapters.TIMESTAMP_FACTORY);
        factories.add(ArrayTypeAdapter.FACTORY);
        factories.add(TypeAdapters.CLASS_FACTORY);

        // type adapters for composite and user-defined types
        factories.add(new CollectionTypeAdapterFactory(constructorConstructor));
        factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization));
        this.jsonAdapterFactory = new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor);
        factories.add(jsonAdapterFactory);
        factories.add(TypeAdapters.ENUM_FACTORY);
        factories.add(new ReflectiveTypeAdapterFactory(
                constructorConstructor, fieldNamingStrategy, excluder, jsonAdapterFactory));

        this.factories = Collections.unmodifiableList(factories);
}

,当我们传入一个User.class类型的时候,Gson会通过ReflectiveTypeAdapterFactory工厂来生产一个Adapter来适配Json对象:

public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
    Class<? super T> raw = type.getRawType();

    if (!Object.class.isAssignableFrom(raw)) {//拦截基本类型
      return null; // it's a primitive!
    }

    ObjectConstructor<T> constructor = constructorConstructor.get(type);//获取构造器
    return new Adapter<T>(constructor, getBoundFields(gson, type, raw));
  }

其他

Gson gson = new GsonBuilder()
        //序列化null
        .serializeNulls()
        // 设置日期时间格式,另有2个重载方法
        // 在序列化和反序化时均生效
        .setDateFormat("yyyy-MM-dd")
        // 禁此序列化内部类
        .disableInnerClassSerialization()
        //生成不可执行的Json(多了 )]}' 这4个字符)
        .generateNonExecutableJson()
        //禁止转义html标签
        .disableHtmlEscaping()
        //格式化输出
        .setPrettyPrinting()
        .create();
上一篇下一篇

猜你喜欢

热点阅读