美文网首页
  Gson框架学习

  Gson框架学习

作者: Nj_第一批老去的90后 | 来源:发表于2017-04-26 17:07 被阅读152次

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

    介绍

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

    基本概念

    • Serialization:序列化,使Java对象到Json字符串的过程。
    • Deserialization:反序列化,字符串转换成Java对象。
    • JSON数据中的JsonElement有下面这四种类型:
    JsonPrimitive —— 例如一个字符串或整型
    JsonObject—— 一个以 JsonElement 名字(类型为 String)作为索引的集合。也就是说可以把 JsonObject 看作值为 JsonElement 的键值对集合。
    JsonArray—— JsonElement 的集合。注意数组的元素可以是四种类型中的任意一种,或者混合类型都支持。
    JsonNull—— 值为null
    

    gson解决的问题

    • 提供一种像toString()和构造方法的很简单的机制,来实现Java 对象和Json之间的互相转换。
    • 允许已经存在的无法改变的对象,转换成Json,或者Json转换成已存在的对象。
    • 允许自定义对象的表现形式;
    • 支持任意的复杂对象;
    • 能够生成可压缩和可读的Json的字符串输出。

    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();
    

    相关文章

      网友评论

          本文标题:  Gson框架学习

          本文链接:https://www.haomeiwen.com/subject/qqqwzttx.html