Gson -反序列化或默认

hvvq6cgz  于 2022-11-06  发布在  其他
关注(0)|答案(2)|浏览(239)

我上课:

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

我想做gson.fromJson("{\"blabla\":null}", Stam::class.java)
但是,它将失败,因为blabla不可为空。
我希望这样,如果gson未能反序列化某个变量,它将采用我给予它的默认值。
如何做到这一点?

kqhtkvqz

kqhtkvqz1#

我认为GSON不可能做到这一点,这也是创建kotlinx.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}")
hm2xizp9

hm2xizp92#

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

  • Gson没有提到Kotlin,它的运行时和细节,所以你最好使用一个更方便的和Kotlin感知的工具。如何检测数据类(如果真的很重要,可以在Kotlin中轻松完成),如何在运行时检测非空参数和字段,等等。
  • 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不是这里最好的选择。

相关问题