[英]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 中声明消息的模型并拥有其中一种实现。
fromJson
的共同点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.