Gson通用反序列化JSON

whhtz7ly  于 2022-11-06  发布在  其他
关注(0)|答案(1)|浏览(114)

我想用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

第二次尝试,我看到了泛型和任意类型的解决方案,但与第一次尝试非常相似。我需要声明类型,而不是寻找类的区别。这在编写的代码中非常相似。
我认为更容易拥有和维护的是,在一些包中声明消息的模型,并拥有这些实现之一。
1.使用基类/接口作为fromJson的公共接地
1.在某个地方注册支持的模型,这样我就可以执行简单的解析器,例如gson.fromJson(messageString)
有没有这样或那样的方法来实现一个更优雅的解决方案?

vom3gejh

vom3gejh1#

由于“contains”操作所需的线性复杂度搜索(不像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();

相关问题