gpt4 book ai didi

java - Gson反序列化通用类型适配器的基类

转载 作者:行者123 更新时间:2023-11-30 10:18:50 25 4
gpt4 key购买 nike

我有以下类(class):

public class Kit {
private String name;
private int num;
}

我有一个扩展 Kit 附加功能的类:

public class ExtendedKit extends Kit {
private String extraProperty;
}

使用 Gson,我希望能够反序列化这两个类以及更多不同类型,而无需为它们创建一堆类型适配器,因为它们都具有相同的 Json 结构:

{
"type": "com.driima.test.ExtendedKit",
"properties": {
"name": "An Extended Kit",
"num": 124,
"extra_property": "An extra property"
}
}

它被传递到注册到我的 GsonBuilder 的以下类型适配器中:

public class GenericAdapter<T> implements JsonDeserializer<T> {
@Override
public T deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
final JsonObject object = json.getAsJsonObject();
String classType = object.get("type").getAsString();
JsonElement element = object.get("properties");

try {
return context.deserialize(element, Class.forName(classType));
} catch (ClassNotFoundException e) {
throw new JsonParseException("Unknown element type: " + type, e);
}
}
}

事情是,它适用于 ExtendedKit,但如果我只想反序列化一个 Kit 而没有 extraProperty,它就不起作用,因为它调用会导致 NullPointerException当它尝试在属性对象上调用 context.deserialize() 时。有什么办法可以解决这个问题吗?


这是我正在使用的 GsonBuilder 的代码:

private static final GsonBuilder GSON_BUILDER = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.registerTypeAdapterFactory(new PostProcessExecutor())
.registerTypeAdapter(Kit.class, new GenericAdapter<Kit>());

注意:添加了 PostProcessExecutor,以便我可以将后处理应用到我反序列化的任何可以进行后处理的对象。有一篇文章here这有助于我实现该功能。

最佳答案

我不认为JsonDeserializer在这里是一个不错的选择:

  • 您需要将每种类型绑定(bind)到 Gson GsonBuilder 中的实例这是一种容易出错的方法,或者使用 registerTypeHierarchyAdapter .
  • 对于后者,您会遇到无限递归(如果我没记错的话:因为上下文只提供一种机制来反序列化相同类型的实例)。

以下类型的适配器工厂可以克服上述限制:

final class PolymorphicTypeAdapterFactory
implements TypeAdapterFactory {

// Let's not hard-code `Kit.class` here and let a user pick up types at a call-site
private final Predicate<? super Class<?>> predicate;

private PolymorphicTypeAdapterFactory(final Predicate<? super Class<?>> predicate) {
this.predicate = predicate;
}

static TypeAdapterFactory get(final Predicate<? super Class<?>> predicate) {
return new PolymorphicTypeAdapterFactory(predicate);
}

@Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
final Class<? super T> rawClass = typeToken.getRawType();
if ( !predicate.test(rawClass) ) {
// Something we cannot handle? Try pick the next best type adapter factory
return null;
}
// This is what JsonDeserializer fails at:
final TypeAdapter<T> writeTypeAdapter = gson.getDelegateAdapter(this, typeToken);
// Despite it's possible to use the above type adapter for both read and write, what if the `type` property points to another class?
final Function<? super Class<T>, ? extends TypeAdapter<T>> readTypeAdapterResolver = actualRawClass -> {
if ( !rawClass.isAssignableFrom(actualRawClass) ) {
throw new IllegalStateException("Cannot parse as " + actualRawClass);
}
return gson.getDelegateAdapter(this, TypeToken.get(actualRawClass));
};
return PolymorphicTypeAdapter.get(rawClass, writeTypeAdapter, readTypeAdapterResolver);
}

private static final class PolymorphicTypeAdapter<T>
extends TypeAdapter<T> {

private final Class<? super T> rawClass;
private final TypeAdapter<T> writeTypeAdapter;
private final Function<? super Class<T>, ? extends TypeAdapter<T>> readTypeAdapterResolver;

private PolymorphicTypeAdapter(final Class<? super T> rawClass, final TypeAdapter<T> writeTypeAdapter,
final Function<? super Class<T>, ? extends TypeAdapter<T>> readTypeAdapterResolver) {
this.rawClass = rawClass;
this.writeTypeAdapter = writeTypeAdapter;
this.readTypeAdapterResolver = readTypeAdapterResolver;
}

// Since constructors are meant only to assign parameters to fields, encapsulate the null-safety handling in the factory method
private static <T> TypeAdapter<T> get(final Class<? super T> rawClass, final TypeAdapter<T> writeTypeAdapter,
final Function<? super Class<T>, ? extends TypeAdapter<T>> readTypeAdapterResolver) {
return new PolymorphicTypeAdapter<>(rawClass, writeTypeAdapter, readTypeAdapterResolver)
.nullSafe();
}

@Override
@SuppressWarnings("resource")
public void write(final JsonWriter jsonWriter, final T value)
throws IOException {
jsonWriter.beginObject();
jsonWriter.name("type");
jsonWriter.value(rawClass.getName());
jsonWriter.name("properties");
writeTypeAdapter.write(jsonWriter, value);
jsonWriter.endObject();
}

@Override
public T read(final JsonReader jsonReader)
throws IOException {
jsonReader.beginObject();
// For simplicity's sake, let's assume that the class property `type` always precedes the `properties` property
final Class<? super T> actualRawClass = readActualRawClass(jsonReader);
final T value = readValue(jsonReader, actualRawClass);
jsonReader.endObject();
return value;
}

private Class<? super T> readActualRawClass(final JsonReader jsonReader)
throws IOException {
try {
requireProperty(jsonReader, "type");
final String value = jsonReader.nextString();
@SuppressWarnings("unchecked")
final Class<? super T> actualRawClass = (Class<? super T>) Class.forName(value);
return actualRawClass;
} catch ( final ClassNotFoundException ex ) {
throw new AssertionError(ex);
}
}

private T readValue(final JsonReader jsonReader, final Class<? super T> rawClass)
throws IOException {
requireProperty(jsonReader, "properties");
@SuppressWarnings("unchecked")
final Class<T> castRawClass = (Class<T>) rawClass;
final TypeAdapter<T> readTypeAdapter = readTypeAdapterResolver.apply(castRawClass);
return readTypeAdapter.read(jsonReader);
}

private static void requireProperty(final JsonReader jsonReader, final String propertyName)
throws IOException {
final String name = jsonReader.nextName();
if ( !name.equals(propertyName) ) {
throw new JsonParseException("Unexpected property: " + name);
}
}

}

}

一个特定于您的 Kit 的使用示例仅类(下面的方法引用仅检查 Kit 是否是给定实际原始类的父类(super class),或者后者是 Kit 本身):

private static final Gson gson = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.registerTypeAdapterFactory(PolymorphicTypeAdapterFactory.get(Kit.class::isAssignableFrom))
.create();

请注意,您的问题不是唯一的,您的案例几乎都被 RuntimeTypeAdapterFactory 覆盖了,但是RuntimeTypeAdapterFactory不隔离 typeproperties就像你的例子一样。

P.S. 请注意,此类型适配器工厂远非真正通用:它不适用于类型(类是类型的特例),generic 类型等。如果有兴趣,但当然不是过度工程,您可能想引用我的编码类型及其参数化解决方案,使用 Type实例 object serialization mechanism (过于隐晦且与特定平台紧密绑定(bind))或使用类型和通用类型表示法 parsing using JParsec (两个链接都指向俄语 StackExchange 网站)。

关于java - Gson反序列化通用类型适配器的基类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49034150/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com