Gson源码分析——(贰)初探

基于非墨上一篇关于泛型的文章,不知道大家是否已经做好了阅读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对象。为了我们开发的方便,一般这类对象我们选择JavaBean格式的对象模型。
在上述代码中,gson相当于起到了一个数据适配器的作用,将一个Json的数据格式转换为一个Java业务可用的数据模型。而这一层的转换,是通过Gson的fromJson方法实现的。

//code1 Gson.java
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。同时,我们也可以看到,实际上我们声明的泛型,并没有受到Type类型的不同而限制。什么意思呢?就是你完全可以写出以下这种形式的代码,也不会产生编译错(当然可能产生运行错):

  Gson gson = new Gson();
  String str = gson.fromJson("{}", Object.class);

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标准语法
      reader.peek();
      isEmpty = false;
      TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);
      TypeAdapter<T> typeAdapter = getAdapter(typeToken);
      T object = typeAdapter.read(reader);
      return object;
      ....
  }

在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解析的时候将以何种形式解析。

//code 3 JsonReader
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 {
    int peekStack = stack[stackSize - 1];
....
 if (peekStack == JsonScope.EMPTY_DOCUMENT) {//line 1
      if (lenient) {
        consumeNonExecutePrefix();
      }
      stack[stackSize - 1] = JsonScope.NONEMPTY_DOCUMENT;
      ...
    }
    ....

    int c = nextNonWhitespace(true);//line2
    switch (c) {
   ....
    case '{':
      return peeked = PEEKED_BEGIN_OBJECT;//line3
    default:
      pos--; // Don't consume the first character in a literal value.
    }
   ....
  }

在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中的标识符,实际上是一种多对应的数据关系,所以不能混用。另外,我们在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对象。

TypeToken(Type type) {
    this.type = $Gson$Types.canonicalize($Gson$Preconditions.checkNotNull(type)/*验证*/);/*生成Gson内部的type*/
    this.rawType = (Class<? super T>) $Gson$Types.getRawType(this.type);
    this.hashCode = this.type.hashCode();
  }

Gson代码结构中,以'$'打头的类用来标识那些只有静态方法的工具类:
1.$Gson$Preconditions:用于验证一些参数信息
2.$Gson$Types :相当于类适配器,将Java的Type实现类型转化为Gson自己的Type类型实现对象
在TypeToken中,有两个非常重要的变量,一个是type,一个是rawType。举个简单的例子来说明一下这两个变量的含义:

TypeToken token = new TypeToken<List<User>>(){}

我们通过一个匿名类来生成一个TypeToken对象,这里我们传入一个泛型的参数List<User>。那么这个TypeToken对应的type类型就是List<User>,而对应的rawType就是List。通过这个简单的理解,我们来看下Gson具体是如何做的:

//code $Gson$Types.java
public static Type canonicalize(Type type) {
    if (type instanceof Class) {
      Class<?> c = (Class<?>) type;
      return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c;

    } else if (type instanceof ParameterizedType) {
      ParameterizedType p = (ParameterizedType) type;
      return new ParameterizedTypeImpl(p.getOwnerType(),
          p.getRawType(), p.getActualTypeArguments());

    } else if (type instanceof GenericArrayType) {
      GenericArrayType g = (GenericArrayType) type;
      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;
    }
  }

Gson通过$Gson$Types的canonicalize方法,将原本定义在JDK里面的Type实现,转化为自己内部的数据实现。主要还是为了操作数据的方便。这里解释一下涉及泛型的一些函数:
1.getOwnerType: 代表拥有者类型,比如你的类型是一个内部类,那么这个Type就指向的是你外部类的类型
2.getRawType : 指向泛型的最外部的类型,比如List<User>指向的就是List类型
3.getGenericComponentType : 指向数组成员类型
4.Gson中将Array类型的Class也归纳为GenericArrayTypeImpl类型
5.getEnclosingClass: 获得外部类

我们重点来看一下ParameterizedType类型

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]);
      }
    }

ParameterizedTypeImpl对象,构造的前半部分代码,将判断此类型是否是静态内部类。为什么需要做这一步操作呢?因为非静态内部类,需要通过外部类的对象才能生成,而Gson目前还没有传入一个外部类对象的接口。
之后,再生成一个外部类的Type变量ownerType,以及最外层类型rawType
之后,通过递归的方式生成typeArguments的类型

我们回到刚才user的例子:

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

由于User类是一个很纯粹的类,因此,在canonicalize方法调用的时候,并不会构建Gson内部的类型,而是会直接返回Class类型。那么当TypeToken.get(User.class)的时候,返回的就是一个内部持有一个User.class为Type的TypeToken变量。
这时候我们再回到code2中(Gson.fromJson方法中),在通过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
Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get();
    boolean requiresThreadLocalCleanup = false;
    if (threadCalls == null) {
      threadCalls = new HashMap<TypeToken<?>, FutureTypeAdapter<?>>();
      calls.set(threadCalls);
      requiresThreadLocalCleanup = true;
    }

当cache中并不存在需要的适配器的时候,并且该线程并不存在有缓冲池对象的时候,Gson将构造一个Map对象,放入线程的中。同事我们还可以发现Gson的Cache的特点:
1.在Gson的Cache管理里,实际上应用了二级缓冲
2.Gson对象内部有一个缓冲,而Gson对象所在的线程又有一个共享Cache。
3.在不同的线程之间,又使用不同的cache。
是否有某位看官有这样的疑问,为什么在Gson的管理里会有3级的Cache?

首先,为什么Gson自身有一个Cache呢?明明可以直接从线程Cache中获取?
这个是因为Gson可能被另外一个线程访问,而如果有了Gson的对象Cache就可以实现共享。

为什么需要多线程之间的Cache?
这个问题非墨是基于一种猜测,多线程间的Cache不一定可以省略,原因是因为如果Adapter在转换数据过程中存在有状态位,很有可能造成数据不同步(但其实这个,完全可以通过拷贝的方式解决。这对提升Gson的效率是很有帮助的)
getAdapter方法之后是方法的核心:

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的构造器里:

// built-in type adapters that cannot be overridden
    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);
    ....
    this.factories = Collections.unmodifiableList(factories);

我们不妨挑一些工厂来看一下,我们来看下excluder这个工厂。excluder是用于拦截一些过期的Gson类型。它的实现类是:Excluder类:

//code Excluder.java
 // function:public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> type)
    Class<?> rawType = type.getRawType();
    final boolean skipSerialize = excludeClass(rawType, true);
    final boolean skipDeserialize = excludeClass(rawType, false);
if (!skipSerialize && !skipDeserialize) {
      return null;
    }

代码中可以看出,Excluder类是通过excludeClass方法,来判断是否exclude掉该类,我们再看一下excludeClass方法:

public boolean excludeClass(Class<?> clazz, boolean serialize) {
    if (version != Excluder.IGNORE_VERSIONS
        && !isValidVersion(clazz.getAnnotation(Since.class), clazz.getAnnotation(Until.class))) {//判断类注解
      return true;
    }

    if (!serializeInnerClasses && isInnerClass(clazz)) {//判断是否是内部类,并且增加忽略内部类标注
      return true;
    }

    if (isAnonymousOrLocal(clazz)) {//判断是否是匿名类,或者是局部类
      return true;
    }

    List<ExclusionStrategy> list = serialize ? serializationStrategies : deserializationStrategies;
    for (ExclusionStrategy exclusionStrategy : list) {//通过判断策略来判断是否忽略类型
      if (exclusionStrategy.shouldSkipClass(clazz)) {
        return true;
      }
    }
    return false;
  }

Gson将会用一堆的策略接口,来判断你的类是否被skip。当Excluder 确定skip掉你的类的时候,excludeClass将返回true结果。代码执行之后,将返回一个匿名的TypeAdapter对象:

return new TypeAdapter<T>() {
      /** The delegate is lazily created because it may not be needed, and creating it may fail. */
      private TypeAdapter<T> delegate;

      @Override public T read(JsonReader in) throws IOException {
        if (skipDeserialize) {
          in.skipValue();
          return null;
        }
        return delegate().read(in);
      }

      @Override public void write(JsonWriter out, T value) throws IOException {
        if (skipSerialize) {
          out.nullValue();
          return;
        }
        delegate().write(out, value);
      }

      private TypeAdapter<T> delegate() {
        TypeAdapter<T> d = delegate;
        return d != null
            ? d
            : (delegate = gson.getDelegateAdapter(Excluder.this, type));
      }
    };
  }

而这个匿名的对象,将通过一个代理对象delegate(通过gson.getDelegateAdapter(Excluder.this, type)获得)来适配对象。

我们回到上面User和ClassRoom的例子,当我们传入一个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));
  }

我们可以通过上面的代码得到以下的结论:
1.这个工厂类的将针对所有非基本类型的类型进行拦截,然后通过内部的Adapter类进行转换
2.而在转换的过程中,需要传入一个ObjectConstructor类型的对象用于构造对象。而这个构造器的获取,源自于一个constructorConstructor的变量。
这里,我们应该不论从名字上还是从什么方面,都能很清楚的知道ObjectConstructor和constructorConstructor的用意。
那么为什么需要一个Constructor的构造器呢?你直接通过类型反射不就完成类型的构造?
原因是因为Gson是支持接口类型,比如List<?>这种集合类。而接口类型转化为Java的实际对象的方式主要有两种:
一种是常见的提供实现类,另一种是生成接口类型的动态代理。而通过动态代理的方案明显不合适,因此Gson在处理这种接口类型的时候,默认提供了一些实现类。
constructorConstructor对象在ReflectiveTypeAdapterFactory构造的时候传入,而所有TypeAdapter的构造工厂都在Gson对象初始化的时候构造。

Gson(final Excluder excluder, final FieldNamingStrategy fieldNamingPolicy,
      final Map<Type, InstanceCreator<?>> instanceCreators, boolean serializeNulls,
      boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe,
      boolean prettyPrinting, boolean serializeSpecialFloatingPointValues,
      LongSerializationPolicy longSerializationPolicy,
      List<TypeAdapterFactory> typeAdapterFactories) {
    this.constructorConstructor = new ConstructorConstructor(instanceCreators);
...
}

constructorConstructor的构造,传入了一个instanceCreators对象,实际上,这个对象是一个享元对象。但默认的情况下,这个对象为空,如果你有自己构造一个对象的需求,可以注入这个对象。那么,Gson会优先选择你注入的这个对象构造器来改造最终对象,为了说明这点,我们来看下ConstructorConstructor的
ObjectConstructor<T> get(TypeToken<T> typeToken)方法:

public <T> ObjectConstructor<T> get(TypeToken<T> typeToken) {
    final Type type = typeToken.getType();
    final Class<? super T> rawType = typeToken.getRawType();

    // first try an instance creator

    @SuppressWarnings("unchecked") // types must agree
    final InstanceCreator<T> typeCreator = (InstanceCreator<T>) instanceCreators.get(type);//先在instanceCreators 查找是否有对应的构造器
    if (typeCreator != null) {
      return new ObjectConstructor<T>() {
        public T construct() {
          return typeCreator.createInstance(type);
        }
      };
    }

    // Next try raw type match for instance creators
    @SuppressWarnings("unchecked") // types must agree
    final InstanceCreator<T> rawTypeCreator =
        (InstanceCreator<T>) instanceCreators.get(rawType);
    if (rawTypeCreator != null) {
      return new ObjectConstructor<T>() {
        public T construct() {
          return rawTypeCreator.createInstance(type);
        }
      };
    }
//如果没有找到构造器,将使用默认构造器
    ObjectConstructor<T> defaultConstructor = newDefaultConstructor(rawType);
    if (defaultConstructor != null) {
      return defaultConstructor;
    }

    ObjectConstructor<T> defaultImplementation = newDefaultImplementationConstructor(type, rawType);
    if (defaultImplementation != null) {
      return defaultImplementation;
    }

    // finally try unsafe
    return newUnsafeAllocator(type, rawType);
  }

代码刚开始,将通过instanceCreators拦截,一旦拦截失败,如果是普通类,将使用默认构造器,如果是接口类将通过newDefaultImplementationConstructor方法,来获取默认接口实现类。

private <T> ObjectConstructor<T> newDefaultConstructor(Class<? super T> rawType) {
    try {
      final Constructor<? super T> constructor = rawType.getDeclaredConstructor();
      if (!constructor.isAccessible()) {
        constructor.setAccessible(true);
      }
      return new ObjectConstructor<T>() {
        @SuppressWarnings("unchecked") // T is the same raw type as is requested
        public T construct() {
          try {
            Object[] args = null;
            return (T) constructor.newInstance(args);
            ....
    }
  }

可见,普通类的构造,Gson将通过一个无参数的构造器来反射调用。而非普通类型将调用newDefaultImplementationConstructor方法来生成一些默认的构造器

private <T> ObjectConstructor<T> newDefaultImplementationConstructor(
      final Type type, Class<? super T> rawType) {//集合类检测
    if (Collection.class.isAssignableFrom(rawType)) {
      if (SortedSet.class.isAssignableFrom(rawType)) {
        return new ObjectConstructor<T>() {
          public T construct() {
            return (T) new TreeSet<Object>();//排序集合实现类
          }
        };
      } else if (EnumSet.class.isAssignableFrom(rawType)) {
        return new ObjectConstructor<T>() {
          @SuppressWarnings("rawtypes")
          public T construct() {
            if (type instanceof ParameterizedType) {
              Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
              if (elementType instanceof Class) {
                return (T) EnumSet.noneOf((Class)elementType);
              } else {
                throw new JsonIOException("Invalid EnumSet type: " + type.toString());
              }
            } else {
              throw new JsonIOException("Invalid EnumSet type: " + type.toString());
            }
          }
        };
      } else if (Set.class.isAssignableFrom(rawType)) {
        return new ObjectConstructor<T>() {
          public T construct() {
            return (T) new LinkedHashSet<Object>();//集合类默认实现
          }
        };
      } else if (Queue.class.isAssignableFrom(rawType)) {
        return new ObjectConstructor<T>() {
          public T construct() {
            return (T) new LinkedList<Object>();//队列默认实现
          }
        };
      } else {
        return new ObjectConstructor<T>() {
          public T construct() {
            return (T) new ArrayList<Object>();//否则统一用ArrayList来实现集合类
          }
        };
      }
    }

    if (Map.class.isAssignableFrom(rawType)) {
      if (SortedMap.class.isAssignableFrom(rawType)) {
        return new ObjectConstructor<T>() {
          public T construct() {
            return (T) new TreeMap<Object, Object>();
          }
        };
      } else if (type instanceof ParameterizedType && !(String.class.isAssignableFrom(
          TypeToken.get(((ParameterizedType) type).getActualTypeArguments()[0]).getRawType()))) {
        return new ObjectConstructor<T>() {
          public T construct() {
            return (T) new LinkedHashMap<Object, Object>();
          }
        };
      } else {
        return new ObjectConstructor<T>() {
          public T construct() {
            return (T) new LinkedTreeMap<String, Object>();
          }
        };
      }
    }

    return null;
  }

我们今天先初探到这里,后面的文章,非墨会深入到Gson是如何解析的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,924评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,781评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,813评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,264评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,273评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,383评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,800评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,482评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,673评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,497评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,545评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,240评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,802评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,866评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,101评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,673评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,245评论 2 341

推荐阅读更多精彩内容