gson的难点问题

2018-11-12  本文已影响0人  byamao1

Circular references

question:循环引用会引起stack overflow
solution:这个目前gson(2.8.5)都没有解决,推荐YaGson,是gson的一个fork,用法相同。

<dependency>
    <groupId>com.gilecode.yagson</groupId>
    <artifactId>yagson</artifactId>
    <version>0.4.1</version>
</dependency>

https://github.com/amogilev/yagson/blob/master/UserGuide.adoc

异常

java.lang.IllegalArgumentException: Infinity is not a valid double value as per JSON specification. To override this behavior, use GsonBuilder.serializeSpecialFloatingPointValues() method.

reason:如果你传入 Float.POSITIVE_INFINITY 值,Gson 将会抛出异常,因为这个值是不能符合 JSON 标准的。
solution:通过 GsonBuilder 设置 serializeSpecialFloatingPointValues() 。
example:

Gson gson = new GsonBuilder()
                .setPrettyPrinting()
                .serializeSpecialFloatingPointValues() // 这是关键
                .create();

https://www.jianshu.com/p/83eb1b2bc119

java.lang.IllegalArgumentException: JSON forbids NaN
and infinities: NaN

test:在这里无论是gson还是YaGson都是报错。

    @Test
    public void testInfinity2() {
        Map map = new HashMap();
        map.put(Double.NaN, "x");
        Gson gson = new YaGsonBuilder()
                .setPrettyPrinting()
//                .enableComplexMapKeySerialization()
                .serializeSpecialFloatingPointValues()
                .create();
        String json = gson.toJson(map);
        log.info(json);

        Map map2 = gson.fromJson(json, HashMap.class);

    }

https://github.com/ar-android/google-gson/issues/639

reason:虽然GsonBuilder可以通过setLenient将JsonWriter.lenient = true:

        Gson gson = new GsonBuilder()
                .setPrettyPrinting()
                .setLenient()   // here
                .serializeSpecialFloatingPointValues()
                .create();

但是JsonTreeWriter.lenient仍然是false。(JsonTreeWriter继承自JsonWriter),通过查看源码得知原因:

TypeAdapter类:
  public final JsonElement toJsonTree(T value, WriteContext ctx) {
    try {
       // new的JsonTreeWriter.lenient为false,此后再无更改
      JsonTreeWriter jsonWriter = new JsonTreeWriter();  
      write(jsonWriter, value, ctx);
      return jsonWriter.get();
    } catch (IOException e) {
      throw new JsonIOException(e);
    }
  }

这就没办法了,只能改源码(未经过全面检测,后果自负):

JsonTreeWriter类:
  public JsonTreeWriter() {
    super(UNWRITABLE_WRITER);
    this.setLenient(true);  //  here,简单粗暴
  }

类型接管

注意:

ImmutableList序列化的类型实际是RegularImmutableList不能使用父类ImmutableList来替上面的子类型RegularImmutableList可以使用registerTypeHierarchyAdapter,方法同registerTypeAdapter
https://www.jianshu.com/p/3108f1e44155

对于又有Circular references又有ImmutableList类型的成员反序列化,只能改YaGson源码。

以下是正常反序列化
com.google.gson.internal.bind
CollectionTypeAdapterFactory.readOptionallyAdvisedInstance line257
MapTypeAdapterFactory.readOptionallyAdvisedInstance line239

以下是引用变量的反序列化
com.google.gson.internal.bind
ReflectiveTypeAdapterFactory$DefaultBoundField

// 加入了对各种guava不变类的判断
    protected void applyReadFieldValue(Object value, Object fieldValue) throws IllegalAccessException {
      if (fieldValue != null || !isPrimitive) {
        Class clazz = field.getType();
        if (clazz.equals(ImmutableList.class)) {
          field.set(value, ImmutableList.copyOf((Collection<?>) fieldValue));
        } else if (clazz.equals(ImmutableSet.class)) {
          field.set(value, ImmutableSet.copyOf((Collection<?>) fieldValue));
        } else if (clazz.equals(ImmutableMap.class)) {
          field.set(value, ImmutableMap.copyOf((Map<?, ?>) fieldValue));
        } else {
          field.set(value, fieldValue);
        }
      }
    }
上一篇 下一篇

猜你喜欢

热点阅读