Gson源码解析和一些实用技巧

1,052 阅读6分钟

1. Json解析的场景和解决方案

1.1 场景

1 先上一个栗子(不含null字段的解析)

val jsonString = "{\"name\":\"woochen\",\"obj\":{\"address\":\"china\"},\"list\":[{\"address\":\"china\"}]}"
data class TestBean(
    val name:String="",
    val obj:InnerBean=InnerBean(),
    val list:List<InnerBean> = listOf()
){
    data class InnerBean(
        val address:String=""
    )
}

注:实体类中字段赋初始值只在json字段没被解析时生效

val result = gson.fromJson<TestBean>(jsonString, TestBean::class.java)
println("解析结果:${result}")

结果:

解析结果:TestBean(name=woochen, obj=InnerBean(address=china), list=[InnerBean(address=china)])

2 再来一个栗子(含null字段的数据解析) 如果服务端没有特别处理,我们可能会接收到这样的json

val jsonString = "{\"name\":null,\"obj\":null,\"list\":null}"

我们现在还是用原来的实体类来解析,结果是这样的

TestBean(name=null, obj=null, list=null)

看似没毛病,我们接着执行这样的代码

result.name.length

编译可以通过,执行的时候会抛出异常

java.lang.NullPointerException

聪明的小伙伴可能会说,这样写不就好了

result.name?.length

结果却是然并卵。。。

由于Gson是通过使用类的无参构造反射生成实例的,所以成功完成了解析过程,但是kotlin的null检验就导致了使用时的空指针问题。

1.2 解决方案

解决方案一: 让服务端给所以字段都赋上初始值,避免null的出现

如果你可以使用这种方案,本文对你毫无用处,不建议继续阅读。

解决方案二: 使用可空类型的实体类解析

data class TestBean(
    val name:String?="",
    val obj:InnerBean?=InnerBean(),
    val list:List<InnerBean>? = listOf()
){
    data class InnerBean(
        val address:String?=""
    )
}

这样解析后,我们在使用字段的时候,在作为非空参数使用时都要加上?或者!!,个人觉得还是有些麻烦的

解决方案三: 直接在解析过程中将null值进行重新赋值,第三部分将提供具体实现方案

//实体类
data class TestBean(
    val name:String="",
    val obj:InnerBean=InnerBean(),
    val list:List<InnerBean> = listOf()
){
    data class InnerBean(
        val address:String=""
    )
}
//目标json
val jsonString = "{\"name\":null,\"obj\":null,\"list\":null}"

//解析结果
TestBean(name=, obj=InnerBean(address=), list=[])

2. gson源码分析

为了实现方案二,我们先进行简单的源码分析,看看Gson是如何将一个json字符串转为具体的实体类。只需要具体代码实现的请跳过该部分,直接看第三部分

以下只包含核心代码部分,源码部分是java代码,项目代码均为kotlin,阅读起来有任何不适请见谅(ps:用了kotlin实在不想写java代码)。

val result = gson.fromJson<TestBean>(jsonString, TestBean::class.java)
  public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException {
    //1.获取Class的类型
    Object object = fromJson(json, (Type) classOfT);
    return Primitives.wrap(classOfT).cast(object);
  }
  
  public <T> T fromJson(String json, Type typeOfT) throws JsonSyntaxException {
    if (json == null) {
      return null;
    }
    //2.根据传入的字符串数据得到Reader对象(真正进行读写操作的对象)
    StringReader reader = new StringReader(json);
    T target = (T) fromJson(reader, typeOfT);
    return target;
  }

  public <T> T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException {
    JsonReader jsonReader = newJsonReader(json);
    //3.包装Reader对象,用来解析{,null,[等字符
    T object = (T) fromJson(jsonReader, typeOfT);
    assertFullConsumption(object, jsonReader);
    return object;
  }

 public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
    ...
     reader.peek();
      isEmpty = false;
      TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);
      //4.根据类型获得相应的适配器
      TypeAdapter<T> typeAdapter = getAdapter(typeToken);
      //5.借助适配器和Reader对象对json进行解析并返回指定的对象
      T object = typeAdapter.read(reader);
      return object;
    ...
 }

从上面的代码可以看出步骤4是比较关键的一步,它决定了类型的转化方式

 public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
    ...
     for (TypeAdapterFactory factory : factories) {
        //1.遍历factories,如果找到匹配的adpter就直接返回
        TypeAdapter<T> candidate = factory.create(this, type);
        if (candidate != null) {
          call.setDelegate(candidate);
          typeTokenCache.put(type, candidate);
          return candidate;
        }
      }
    ...
}

factories是在Gson的构造中传入的,gson开放了api用来设置factory

//GsonBuilder.java
 public GsonBuilder registerTypeAdapterFactory(TypeAdapterFactory factory) {
    factories.add(factory);
    return this;
  } 
  
public Gson create() {
    List<TypeAdapterFactory> factories = new ArrayList<TypeAdapterFactory>(this.factories.size() + this.hierarchyFactories.size() + 3);
    factories.addAll(this.factories);
    //我们添加的factory被翻转了
    Collections.reverse(factories);
    List<TypeAdapterFactory> hierarchyFactories = new ArrayList<TypeAdapterFactory>(this.hierarchyFactories);
    Collections.reverse(hierarchyFactories);
    factories.addAll(hierarchyFactories);
    addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, factories);
    return new Gson(excluder, fieldNamingPolicy, instanceCreators,
        serializeNulls, complexMapKeySerialization,
        generateNonExecutableJson, escapeHtmlChars, prettyPrinting, lenient,
        serializeSpecialFloatingPointValues, longSerializationPolicy,
        datePattern, dateStyle, timeStyle,
        this.factories, this.hierarchyFactories, factories);
  } 

//Gson.Java
  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, String datePattern, int dateStyle,
      int timeStyle, List<TypeAdapterFactory> builderFactories,
      List<TypeAdapterFactory> builderHierarchyFactories,
      List<TypeAdapterFactory> factoriesToBeAdded) {
       List<TypeAdapterFactory> factories = new ArrayList<TypeAdapterFactory>();

    // built-in type adapters that cannot be overridden
    factories.add(TypeAdapters.JSON_ELEMENT_FACTORY);
    factories.add(ObjectTypeAdapter.FACTORY);
    factories.add(excluder);

    //!!!这里就是我们自己添加的factory
    factories.addAll(factoriesToBeAdded);
    // 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);
    ...
      }

factoriesToBeAdded是通过registerTypeAdapterFactory设置的factory,它们会添加到Gson默认的适配器的前面,而factories是从头开始遍历来匹配adpter的,所以我们可以通过覆盖指定类型的适配器,来走我们自己的adapter的逻辑从而实现需求。

于是我们找其中一个默认adapter来查看解析逻辑,以下是默认对字符串的解析适配器TypeAdapters.STRING_FACTORY

//TypeAdapters.STRING_FACTORY
  public static final TypeAdapter<String> STRING = new TypeAdapter<String>() {
    @Override
    public String read(JsonReader in) throws IOException {
      JsonToken peek = in.peek();
      if (peek == JsonToken.NULL) {
        in.nextNull();
        //!!!当字段值为null时,返回null
        return null;
      }
      /* coerce booleans to strings for backwards compatibility */
      if (peek == JsonToken.BOOLEAN) {
        return Boolean.toString(in.nextBoolean());
      }
      return in.nextString();
    }
    @Override
    public void write(JsonWriter out, String value) throws IOException {
      out.value(value);
    }
  };

比如:我们想要字符串为null时,解析后返回“”,而不是null,于是就可以这样写

class StringNullAdapter : TypeAdapter<String>() {
        @Throws(IOException::class)
        override fun read(reader: JsonReader): String {
            if (reader.peek() === JsonToken.NULL) {
                reader.nextNull()
                //只修改了这里
                return ""
            }
            return reader.nextString()
        }

        @Throws(IOException::class)
        override fun write(writer: JsonWriter, value: String?) {
            if (value == null) {
                writer.nullValue()
                return
            }
            writer.value(value)
        }
    }

3. Gson非空TypeAdapter实现

//NotNullAdapterFactory.kt
class NotNullAdapterFactory : TypeAdapterFactory {
    override fun <T> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
        val rawType = type.rawType as Class<T>
        return if (rawType == String::class.java) {
            StringNullAdapter() as TypeAdapter<T>
        } else if (rawType == Int::class.java || rawType == Integer::class.java) {
            IntNullAdapter() as TypeAdapter<T>
        }else if (rawType == Boolean::class.java) {
            BooleanNullAdapter() as TypeAdapter<T>
        }else if (rawType == Long::class.java) {
            LongNullAdapter() as TypeAdapter<T>
        }else {
            null
        }
    }

    class StringNullAdapter : TypeAdapter<String>() {
        @Throws(IOException::class)
        override fun read(reader: JsonReader): String {
            if (reader.peek() === JsonToken.NULL) {
                reader.nextNull()
                return ""
            }
            return reader.nextString()
        }

        @Throws(IOException::class)
        override fun write(writer: JsonWriter, value: String?) {
            if (value == null) {
                writer.nullValue()
                return
            }
            writer.value(value)
        }
    }

    class IntNullAdapter : TypeAdapter<Int>() {
        @Throws(IOException::class)
        override fun read(reader: JsonReader): Int {
            if (reader.peek() === JsonToken.NULL) {
                reader.nextNull()
                return 0
            }
            return reader.nextInt()
        }

        @Throws(IOException::class)
        override fun write(writer: JsonWriter, value: Int?) {
            if (value == null) {
                writer.nullValue()
                return
            }
            writer.value(value)
        }
    }

    class BooleanNullAdapter : TypeAdapter<Boolean>() {
        @Throws(IOException::class)
        override fun read(reader: JsonReader): Boolean {
            if (reader.peek() === JsonToken.NULL) {
                reader.nextNull()
                return false
            }
            return reader.nextBoolean()
        }

        @Throws(IOException::class)
        override fun write(writer: JsonWriter, value: Boolean?) {
            if (value == null) {
                writer.nullValue()
                return
            }
            writer.value(value)
        }
    }

    class LongNullAdapter : TypeAdapter<Long>() {
        @Throws(IOException::class)
        override fun read(reader: JsonReader): Long {
            if (reader.peek() === JsonToken.NULL) {
                reader.nextNull()
                return 0
            }
            return reader.nextLong()
        }

        @Throws(IOException::class)
        override fun write(writer: JsonWriter, value: Long?) {
            if (value == null) {
                writer.nullValue()
                return
            }
            writer.value(value)
        }
    }

}

其中,对象类型和数组类型比较特殊,可以将默认的ReflectiveTypeAdapterFactory和CollectionTypeAdapterFactory相关的文件拷贝出来进行修改。修改方法于上面相同

最后使用是这样的

val gsonBulder = GsonBuilder()
val f: Field = gsonBulder::class.java.getDeclaredField("instanceCreators")
f.isAccessible = true
val instanceCreator = f.get(gsonBulder) as Map<Type, InstanceCreator<*>>
val constructorConstructor = ConstructorConstructor(instanceCreator)
//注意拦截顺序,上面的源码中看到了,通过registerTypeAdapterFactory添加的factories会被进行反转,所以最后添加的会被最先遍历。
gsonBulder.registerTypeAdapterFactory(ReflectiveTypeAdapterFactory(constructorConstructor, FieldNamingPolicy.IDENTITY, Excluder.DEFAULT,JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor)))
            .registerTypeAdapterFactory(CollectionTypeAdapterFactory(constructorConstructor))
            .registerTypeAdapterFactory(NotNullAdapterFactory())
val gson = gsonBulder.create()

注意:

  1. 以上代码,笔者在项目中亲测可用。
  2. CollectionTypeAdapterFactory和ReflectiveTypeAdapterFactory相关的代码过长且比较简单,就没有贴出来,按照NotNullAdapterFactory中的adpter的方式修改即可。
  3. NotNullAdapterFactory并没有覆盖所有的默认类型,只是笔者项目中使用到的。
  4. 如果使用了ReflectiveTypeAdapterFactory会拦截默认对象的解析,它只适用于自定义的对象解析,自动装箱的Integer和Long等是无法正常解析的,如果待解析的gson中出现了,需要在NotNullAdapterFactory中进行判断,可以参考NotNullAdapterFactory中的代码。

大功告成,再贴一遍方案二

//实体类
data class TestBean(
    val name:String="",
    val obj:InnerBean=InnerBean(),
    val list:List<InnerBean> = listOf()
){
    data class InnerBean(
        val address:String=""
    )
}
//目标json
val jsonString = "{\"name\":null,\"obj\":null,\"list\":null}"

//解析结果
TestBean(name=, obj=InnerBean(address=), list=[])

本人水平有限,如有错误请留言指正。如果本文对你有一点点帮助,请随手点个赞鼓励一下吧