Gson-反序列化或默认



我有一个类:

data class Stam(@SerializedName("blabla") val blabla: String = "")

我想做gson.fromJson("{"blabla":null}", Stam::class.java)

然而,它将失败,因为blabla不可为null。

我想这样做,如果gson未能反序列化某些变量,它将采用我给它的默认值。

如何做到这一点?

我认为GSON不可能,这也是创建kotlin.serialization库的原因之一。有了这个库,它相当容易:

@Serializable
data class Stam(@SerialName("blabla") val blabla: String = "") //actually, @SerialName may be omitted if it is equal to field name
Json { coerceInputValues = true }.decodeFromString<Stam>("{"blabla":null}")

我不会说这在Gson中是不可能的,但Gson绝对不是最好的选择:

  • Gson没有提到Kotlin,它的运行时和细节,所以最好使用一个更方便和Kotlin意识的工具。这里的典型问题是:如何检测数据类(如果它真的很重要,可以在Kotlin中轻松完成(,如何在运行时检测非null参数和字段,等等
  • Kotlin中的数据类似乎提供了Gson可解析的默认构造函数,因此Gson可以调用它(尽管它可以在没有使用不安全机制的构造函数的情况下实例化类实例(;全功能";具有默认参数的构造函数。这里的技巧是从输入JSON中删除空值属性,因此Gson将保留";默认有论证的";字段未受影响

我喜欢Java,但我相信以下代码可以很容易地转换(如果你认为Gson仍然是一个正确的选择(:

final class StripNullTypeAdapterFactory
implements TypeAdapterFactory {
// The rule to check whether this type adapter should be applied.
// Externalizing the rule makes it much more flexible.
private final Predicate<? super TypeToken<?>> isClassSupported;
private StripNullTypeAdapterFactory(final Predicate<? super TypeToken<?>> isClassSupported) {
this.isClassSupported = isClassSupported;
}
static TypeAdapterFactory create(final Predicate<? super TypeToken<?>> isClassSupported) {
return new StripNullTypeAdapterFactory(isClassSupported);
}
@Override
@Nullable
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
if ( !isClassSupported.test(typeToken) ) {
return null;
}
// If the type is supported by the rule, get the type "real" delegate
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, typeToken);
return new StripNullTypeAdapter<>(delegate);
}
private static final class StripNullTypeAdapter<T>
extends TypeAdapter<T> {
private final TypeAdapter<T> delegate;
private StripNullTypeAdapter(final TypeAdapter<T> delegate) {
this.delegate = delegate;
}
@Override
public void write(final JsonWriter out, final T value)
throws IOException {
delegate.write(out, value);
}
@Override
public T read(final JsonReader in) {
// Another disadvantage in using Gson:
// the null-stripped object must be buffered into memory regardless how big it is.
// So it may generate really big memory footprints.
final JsonObject buffer = JsonParser.parseReader(in).getAsJsonObject();
// Strip null properties from the object
for ( final Iterator<Map.Entry<String, JsonElement>> i = buffer.entrySet().iterator(); i.hasNext(); ) {
final Map.Entry<String, JsonElement> property = i.next();
if ( property.getValue().isJsonNull() ) {
i.remove();
}
}
// Now there is no null values so Gson would only use properties appearing in the buffer
return delegate.fromJsonTree(buffer);
}
}
}

测试:

public final class StripNullTypeAdapterFactoryTest {
private static final Collection<Class<?>> supportedClasses = ImmutableSet.of(Stam.class);
private static final Gson gson = new GsonBuilder()
.disableHtmlEscaping()
// I don't know how easy detecting data classes and non-null parameters is
// but since the rule is externalized, let's just lookup it
// in the "known classes" registry
.registerTypeAdapterFactory(StripNullTypeAdapterFactory.create(typeToken -> supportedClasses.contains(typeToken.getRawType())))
.create();
@Test
public void test() {
final Stam stam = gson.fromJson("{"blabla":null}", Stam.class);
// The test is "green" since 
Assertions.assertEquals("", stam.getBlabla());
}
}

我仍然认为Gson不是这里的最佳选择。

最新更新