子墨庖丁-Gson的源码分析(1)-开篇

       因为工作的关系,使用到了Gson,先暂停了书写Proguard的文章,回到Gson的研究。当然也不会总是研究Gson,我会两个项目跳着来。完全按照自己的兴趣来。我个人觉得Gson是一个非常优秀的json解析开源项目,效率高,代码结构也非常的清晰。给使用者提供了非常好的用户体验。封装也非常的优雅。因此,我对Gson的研究充满了兴趣,这里顺带提一下我前两天发现的一个非常不错的Rest封装框架:Retrofit,我不知道知道的人有多少,我之前一直使用的是自己写的Rest封装,代码的复用性也不强,而Retrofit却把这样东西做了极致。我将在后面的文章中研究它。你可能很少不了@子墨这种想到哪写到哪的做法。不过如果你看的顺眼就不妨跟这不成文的节奏走吧,因为我不打算改变我的写作方式。

        回到正题,Gson主要分成两部分,一个就是数据拆解,一个是数据封装。这两个概念我都是基于JSON层的概念而言。这样JSON对象的拆解的结果就是JAVA对象,而所谓的封装就是将一个OO的对象封装成为一个json对象。当然json对象可以理解为就是一个字符串。那么实际上Gson做的事情就是完成String到JAVA对象的映射。我们建立起这个概念以后我们就能明白我们的研究主要分成两个大部分。前几个系列我们会先说String到JAVA对象之间的映射。

      我们导入Gson的源码发现代码比预想的少的多,分包也很明确。你是不是瞬间对Google公司崇敬起来。我们明白,如果要对系统之上构建一个封装的话,用的就是门面模式关于门面模式的说法,大家可以参考我的一篇文章:

子墨对酒《三国杀》里论模式(二)门面模式

构造门面的包名一般都是所有系统的顶层包。我们看到com.google.gson就很好的封装了这些门面。也就是说我们要跟系统打交道,实际上就是要跟这些门面打交道。

     String 到对象的主要门面是Gson.java。里面提供了各种各样的方法方便你调用。

public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
    boolean isEmpty = true;
    boolean oldLenient = reader.isLenient();
    reader.setLenient(true);
    try {
      reader.peek();
      isEmpty = false;
      TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);
      TypeAdapter<T> typeAdapter = getAdapter(typeToken);
      T object = typeAdapter.read(reader);
      System.out.println("object = "+object);
      return object;
    } catch (EOFException e) {
      /*
       * For compatibility with JSON 1.5 and earlier, we return null for empty
       * documents instead of throwing.
       */
      if (isEmpty) {
        return null;
      }
      throw new JsonSyntaxException(e);
    } catch (IllegalStateException e) {
      throw new JsonSyntaxException(e);
    } catch (IOException e) {
      // TODO(inder): Figure out whether it is indeed right to rethrow this as JsonSyntaxException
      throw new JsonSyntaxException(e);
    } finally {
      reader.setLenient(oldLenient);
    }
  }
我们能比较清晰的看到, 实际上对于数据的转换,是由:TypeAdapter<T> typeAdapter这个对象来完成的,我们可以从名字看出它本质上一个适配器。关于适配器模式大家可以参考我的一篇文章:

子墨对酒《三国杀》里论模式(三)适配器模式

为什么需要适配器呢?我们粘出一个适配器的代码相信大家就能了解了:

public final class DateTypeAdapter extends TypeAdapter<Date> {
  public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
    @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
      return typeToken.getRawType() == Date.class ? (TypeAdapter<T>) new DateTypeAdapter() : null;
    }
  };

  private final DateFormat enUsFormat
      = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US);
  private final DateFormat localFormat
      = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT);
  private final DateFormat iso8601Format = buildIso8601Format();

  private static DateFormat buildIso8601Format() {
    DateFormat iso8601Format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
    iso8601Format.setTimeZone(TimeZone.getTimeZone("UTC"));
    return iso8601Format;
  }

  @Override public Date read(JsonReader in) throws IOException {
    if (in.peek() == JsonToken.NULL) {
      in.nextNull();
      return null;
    }
    return deserializeToDate(in.nextString());
  }

  private synchronized Date deserializeToDate(String json) {
    try {
      return localFormat.parse(json);
    } catch (ParseException ignored) {
    }
    try {
      return enUsFormat.parse(json);
    } catch (ParseException ignored) {
    }
    try {
      return iso8601Format.parse(json);
    } catch (ParseException e) {
      throw new JsonSyntaxException(json, e);
    }
  }

  @Override public synchronized void write(JsonWriter out, Date value) throws IOException {
    if (value == null) {
      out.nullValue();
      return;
    }
    String dateFormatAsString = enUsFormat.format(value);
    out.value(dateFormatAsString);
  }
}

         我们可以发现,对于传入的数据类型来说,Gson并不知道什么时候要用它来适配,它的适配要完全依赖于这些适配器。当你传入的是适配器判断条件所需的字节码条件的时候,就将采用该种适配器来进行适配但是令人非常遗憾的是:在gson这个门面的设计的时候,并没有提供接口用来factory的扩展,这样你就得完全按照Gson的那一套规则而且对于一个对象的生成采用成文的规定,完全没有给外界修改的权利。但是这并不会影响Gson作为一个出色的项目,优雅的代码结构而存在。

       代码中让我们非常崩溃的就是大量的匿名类,不过Gson似乎也意识到了这个问题:

public static <TT> TypeAdapterFactory newFactory(
      final Class<TT> type, final TypeAdapter<TT> typeAdapter) {
    return new TypeAdapterFactory() {
      @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
      public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
        return typeToken.getRawType() == type ? (TypeAdapter<T>) typeAdapter : null;
      }
      @Override public String toString() {
        return "Factory[type=" + type.getName() + ",adapter=" + typeAdapter + "]";
      }
    };
  }

  我们发现它使用通用的模板来生成类,无可厚非。而且也复写了toString为了方便调试。我写了一段代码来做测试:

/**
 * @author 非子墨
 * */
public class TestGson {
	public static class User{
		public String name;
		public int age;
		public Info info;
		@Override
		public String toString() {
			// TODO Auto-generated method stub
			return "["+name+":"+info.msg+"]";
		}
	}

	public static class Info{
		public Info() {}
		String msg;
	}
	public static void main(String[] args) {
		String gsonValue = "{name:'非子墨',age:23,info:{msg:'I am a student!'}}";
		Gson gson = new Gson() ;
		User user = gson.fromJson(gsonValue, User.class);
		System.out.println("user = "+user);
	}
}

最后的运行结果是:

Gson:>>>Factory[type=java.lang.String,adapter=com.google.gson.internal.bind.TypeAdapters$13@398020cc]
Gson:>>>Factory[type=java.lang.Integer+int,adapter=com.google.gson.internal.bind
Gson:>>>com.google.gson.internal.bind.ReflectiveTypeAdapterFactory@6030e280
Gson:create type = com.test.TestGson$Info
Gson:>>>com.google.gson.internal.bind.ReflectiveTypeAdapterFactory@6030e280
Gson:create type = com.test.TestGson$User

这里面我 加了一些log,我们可以通过这些log能很明白的理解Gson的总体解析结构,就是顺序解析,如果是对象的话是通过
ReflectiveTypeAdapterFactory
这个类生成的Adapter对象来生成。我们也会发现,作为最终要解析成的对象User, 实际上是最后解析的,因此我们推测,解析完内部对象以后采用组合的方式来注入。至于组合模式,将会在我以后关于<三国杀>设计模式的文章补充到。

由于时间的关系本系列文章将会分成多个小节来写,下一篇我希望还是像Proguard源码分析一样先从Json的数据读取开始,主要的类是:

JsonReader.java


待续。。。

                                                                                                                                                @非子墨
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值