使用带有 lenient = true 设置的 Gson RuntimeTypeAdapterFactory

Use Gson RuntimeTypeAdapterFactory with lenient = true setting

我们摄取 returns 一些格式错误的 JSON 的 API(特别是 Double.NaN 被序列化为 String 和 "NaN")。 为了允许此 gson 始终将 lenient 设置为 true 并将其正确序列化回 Double.NaN.

现在,我们想使用 gson-extras 中的 RuntimeTypeAdapterFactory class 来删除一些样板代码。 刚刚描述的行为不再适用于此,我们收到消息

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

哪个 由于通常从 gson 设置的 lenient 选项,应该被忽略。

考虑这个小例子:

class Base
{
   String type;
   double malformedField;
}

class A extends Base{}
class B extends Base{}

我们使用 type 字段来确定我们想要哪个子class,因为当然否则我们无法推断出正确的类型。

这是我们用来创建子classes 和反序列化的设置:

RuntimeTypeAdapterFactory<Base> adapterFactory = RuntimeTypeAdapterFactory.of(Base.class, "type", true)
    .registerSubtype(A.class, "A")
    .registerSubtype(B.class, "B");

Gson gson = new GsonBuilder()
    .registerTypeAdapterFactory(adapterFactory)
    //.setLenient() // Has no effect at all
    .create();

String json = "{\"type\":\"A\", \"malformedField\": \"NaN\"}";
Base a = gson.fromJson(json, Base.class); // throws said Exception

RuntimeTypeAdapterFactory中的实施:

检查 RuntimeTypeAdapterFactory 中的代码后,我注意到 lenient 选项从未设置为 true,因为我们调用了 com.google.gson.TypeAdapterfromJsonTree 方法,在我们创建了一个新的 JsonTreeReader 而无法更改 lenient 选项。

public final T fromJsonTree(JsonElement jsonTree) {
    try {
        JsonReader jsonReader = new JsonTreeReader(jsonTree);
        return read(jsonReader);
    } catch (IOException e) {
        throw new JsonIOException(e);
    }
}

我尝试通过调试手动将其设置为 true,这似乎按预期工作(没有 NumberFormatException 并使用指定类型正确反序列化对象)。

现在的问题是:关于为什么我们不能在此处使用 lenient 选项,我是否遗漏了什么?乍一看,这似乎是 gson-extras 的错误,但我不确定。

这不完全是 gson-extras 的错误,因为 com.google.gson.TypeAdapter class 来自核心库。我建议再注册一个 TypeAdapterFactory 并为所有 JsonReader 个实例强制 lenient

class AlwaysLenientTypeAdapterFactory implements TypeAdapterFactory {

    public final <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
        return createCustomTypeAdapter(delegate);
    }

    private <T> TypeAdapter<T> createCustomTypeAdapter(TypeAdapter<T> delegate) {
        return new TypeAdapter<T>() {
            @Override
            public void write(JsonWriter out, T value) throws IOException {
                delegate.write(out, value);
            }

            @Override
            public T read(JsonReader in) throws IOException {
                in.setLenient(true);
                return delegate.read(in);
            }
        };
    }
}

用法:

Gson gson = new GsonBuilder()
        .registerTypeAdapterFactory(adapterFactory)
        .registerTypeAdapterFactory(new AlwaysLenientTypeAdapterFactory())
        .create();

另请参阅: