use*_*647 8 java generics json types gson
为什么Gson在序列化时会忽略嵌套的泛型类型声明?
我试图让Gson使用我指定的编译时类型,而不是列表中的运行时类型的对象.我也使用抽象超类A.java,但下面的例子有同样的问题.
public class A {
public String foo;
}
public class B extends A {
public String bar;
}
public static void main( String[] args ) {
Gson gson = new Gson();
B b = new B();
b.foo = "foo";
b.bar = "bar";
List<A> list = new ArrayList<A>();
list.add(b);
System.out.println(gson.toJson(b, new TypeToken<A>(){}.getType()));
System.out.println(gson.toJson(b, new TypeToken<B>(){}.getType()));
System.out.println(gson.toJson(list, new TypeToken<List<A>>(){}.getType()));
System.out.println(gson.toJson(list, new TypeToken<List<B>>(){}.getType()));
}
Run Code Online (Sandbox Code Playgroud)
输出:
{"foo":"foo"}
{"bar":"bar","foo":"foo"}
[{"bar":"bar","foo":"foo"}]
[{"bar":"bar","foo":"foo"}]
Run Code Online (Sandbox Code Playgroud)
预期:
{"foo":"foo"}
{"bar":"bar","foo":"foo"}
[{"foo":"foo"}]
[{"bar":"bar","foo":"foo"}]
Run Code Online (Sandbox Code Playgroud)
如果您不关心原因而只想修复,请滚动到底部。
Gson 的默认值CollectionTypeAdapterFactory将其元素类型适配器包装在称为TypeAdapterRuntimeTypeWrapper. 选择合适的适配器时,会优先考虑以下因素:
// Order of preference for choosing type adapters
// First preference: a type adapter registered for the runtime type
// Second preference: a type adapter registered for the declared type
// Third preference: reflective type adapter for the runtime type (if it is a sub class of the declared type)
// Fourth preference: reflective type adapter for the declared type
Run Code Online (Sandbox Code Playgroud)
在这种情况下,第三个首选项是 的适配器B,第四个首选项是 A 的适配器。使用默认序列化器时这是不可避免的,因为中没有条件CollectionTypeAdapterFactory:
public Adapter(Gson context, Type elementType,
TypeAdapter<E> elementTypeAdapter,
ObjectConstructor<? extends Collection<E>> constructor) {
this.elementTypeAdapter =
new TypeAdapterRuntimeTypeWrapper<E>(context, elementTypeAdapter, elementType);
this.constructor = constructor;
}
Run Code Online (Sandbox Code Playgroud)
不使用 时,此包装器不存在CollectionTypeAdapterFactory,这就是为什么它不会在前两个示例中发生。
解决这个问题的唯一方法是注册一个自定义序列化器。在您的用例中,写一个就A可以解决问题:
public class ATypeAdapter extends TypeAdapter<A> {
public A read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
reader.beginObject();
String name = reader.nextName();
if(!"foo".equals(name)) throw new JsonSyntaxException("Expected field named foo");
A a = new A();
a.foo = reader.nextString();
reader.endObject();
return a;
}
public void write(JsonWriter writer, A value) throws IOException {
if (value == null) {
writer.nullValue();
return;
}
writer.beginObject();
writer.name("foo");
writer.value(value.foo);
writer.endObject();
}
}
Run Code Online (Sandbox Code Playgroud)
那么,如果你这样做:
public static void main( String[] args ) {
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(new TypeToken<A>(){}.getType(), new ATypeAdapter());
Gson gson = builder.create();
B b = new B();
b.foo = "foo";
b.bar = "bar";
List<A> list = new ArrayList<A>();
list.add(b);
System.out.println(gson.toJson(b, new TypeToken<A>(){}.getType()));
System.out.println(gson.toJson(b, new TypeToken<B>(){}.getType()));
System.out.println(gson.toJson(list, new TypeToken<List<A>>(){}.getType()));
System.out.println(gson.toJson(list, new TypeToken<List<B>>(){}.getType()));
}
Run Code Online (Sandbox Code Playgroud)
您将得到预期的输出:
// Order of preference for choosing type adapters
// First preference: a type adapter registered for the runtime type
// Second preference: a type adapter registered for the declared type
// Third preference: reflective type adapter for the runtime type (if it is a sub class of the declared type)
// Fourth preference: reflective type adapter for the declared type
Run Code Online (Sandbox Code Playgroud)