繁体   English   中英

Gson 通用反序列化 JSON

[英]Gson generic deserialize JSON

我想使用 Gson 构建一个 websocket 客户端。 正如预期的那样,websocket 通信基于几个不同的消息。

我想有一种优雅的方式将 JSON 字符串转换为声明的模型/对象。

第一次尝试:

if message.contains("fieldA") {
    return gson.fromJson(message, ObjectA.class);
} else if message.contains("fieldB") {
    return gson.fromJson(message, ObjectB.class);
} else if message.contains("fieldC") {
    return gson.fromJson(message, ObjectC.class);
}
// etc

第二次尝试,我看到了通用和任意类型的解决方案,但与第一次尝试非常相似。 而不是寻找区分 Class 我需要声明类型。 这在书面代码中非常相似。

我认为更容易拥有和维护的是在某些 package 中声明消息的模型并拥有其中一种实现。

  1. 使用基类/接口作为fromJson的共同点
  2. 在某处注册支持的模型,以便我可以执行简单的解析器gson.fromJson(messageString)

有这样或其他的方法来实现更优雅的解决方案吗?

由于“包含”操作所需的线性复杂性搜索(与使用鉴别器类型字段的RuntimeTypeAdapterFactory不同),我认为通用解决方案不会有效,但它可能会像这样实现:

@AllArgsConstructor(access = AccessLevel.PRIVATE)
public final class JsonElementTypeAdapterFactoryBuilder<V, J extends JsonElement> {

    private final Class<V> baseType;

    private final Collection<Rule<V, J>> rules = new ArrayList<>();

    public static <V, J extends JsonElement> JsonElementTypeAdapterFactoryBuilder<V, J> construct(final Class<V> baseType) {
        return new JsonElementTypeAdapterFactoryBuilder<>(baseType);
    }

    public JsonElementTypeAdapterFactoryBuilder<V, J> register(final Predicate<? super J> isSupported,
            final Supplier<? extends TypeToken<? extends V>> getTypeToken) {
        rules.add(new Rule<V, J>(isSupported, getTypeToken));
        return this;
    }

    public TypeAdapterFactory build() {
        @SuppressWarnings("unchecked")
        final Rule<V, J>[] rules = this.rules.stream()
                .toArray(Rule[]::new);
        return new TypeAdapterFactory() {
            @Override
            @Nullable
            public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
                if ( !baseType.isAssignableFrom(typeToken.getRawType()) ) {
                    return null;
                }
                final TypeAdapterFactory typeAdapterFactory = this;
                return new TypeAdapter<T>() {
                    private final Map<TypeToken<? extends V>, TypeAdapter<? extends V>> index = new ConcurrentHashMap<>();

                    @Override
                    public void write(final JsonWriter out, final T value) {
                        throw new UnsupportedOperationException();
                    }

                    @Override
                    public T read(final JsonReader in) {
                        @SuppressWarnings("unchecked")
                        final J jsonElement = (J) Streams.parse(in);
                        for ( final Rule<V, J> rule : rules ) {
                            if ( rule.isSupported.test(jsonElement) ) {
                                final TypeToken<? extends V> concreteTypeToken = rule.getTypeToken.get();
                                final TypeAdapter<? extends V> typeAdapter = index.computeIfAbsent(concreteTypeToken, tt -> gson.getDelegateAdapter(typeAdapterFactory, tt));
                                @SuppressWarnings("unchecked")
                                final T value = (T) typeAdapter.fromJsonTree(jsonElement);
                                return value;
                            }
                        }
                        throw new AssertionError(typeToken);
                    }
                };
            }
        };
    }

    @AllArgsConstructor(access = AccessLevel.PRIVATE)
    private static final class Rule<V, J extends JsonElement> {

        private final Predicate<? super J> isSupported;
        private final Supplier<? extends TypeToken<? extends V>> getTypeToken;

    }

}

用下面的例子:

private static final Gson gson = new GsonBuilder()
        .disableHtmlEscaping()
        .disableInnerClassSerialization()
        .registerTypeAdapterFactory(JsonElementTypeAdapterFactoryBuilder.<ICommon, JsonObject>construct(ICommon.class)
                .register(jsonObject -> jsonObject.has("fieldA"), () -> objectATypeToken)
                .register(jsonObject -> jsonObject.has("fieldB"), () -> objectBTypeToken)
                .register(jsonObject -> jsonObject.has("fieldC"), () -> objectCTypeToken)
                .build()
        )
        .create();

我不确定这个实现是否是超级优化的(好吧,我放弃了,它不是),但我想下面的实现会更简单更高效(不管没有 API):

private static final Gson gson = new GsonBuilder()
        .disableHtmlEscaping()
        .disableInnerClassSerialization()
        .registerTypeAdapterFactory(new TypeAdapterFactory() {
            @Override
            @Nullable
            public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
                if ( !ICommon.class.isAssignableFrom(typeToken.getRawType()) ) {
                    return null;
                }
                final TypeAdapter<ObjectA> aTypeAdapter = gson.getDelegateAdapter(this, new TypeToken<ObjectA>() {});
                final TypeAdapter<ObjectB> bTypeAdapter = gson.getDelegateAdapter(this, new TypeToken<ObjectB>() {});
                final TypeAdapter<ObjectC> cTypeAdapter = gson.getDelegateAdapter(this, new TypeToken<ObjectC>() {});
                return new TypeAdapter<T>() {
                    @Override
                    public void write(final JsonWriter out, final T value) {
                        throw new UnsupportedOperationException("TODO");
                    }

                    @Override
                    public T read(final JsonReader in)
                            throws IOException {
                        final JsonElement jsonElement = Streams.parse(in);
                        final JsonObject jsonObject = jsonElement
                                .getAsJsonObject();
                        final TypeAdapter<? extends ICommon> typeAdapter;
                        if ( jsonObject.has("fieldA") ) {
                            typeAdapter = aTypeAdapter;
                        } else if ( jsonObject.has("fieldB") ) {
                            typeAdapter = bTypeAdapter;
                        } else if ( jsonObject.has("fieldC") ) {
                            typeAdapter = cTypeAdapter;
                        } else {
                            throw new JsonParseException("Unsupported JSON element: " + jsonElement);
                        }
                        @SuppressWarnings("unchecked")
                        final T value = (T) typeAdapter.fromJsonTree(jsonElement);
                        return value;
                    }
                }
                        .nullSafe();
            }
        })
        .create();

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM