簡體   English   中英

為什么Gson會序列化列表中的運行時類型,而不是指定編譯時類型?

[英]Why does Gson serializes runtime type in list, not specified compile-time type?

為什么序列化時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()));
}

輸出:

{"foo":"foo"}
{"bar":"bar","foo":"foo"}
[{"bar":"bar","foo":"foo"}]
[{"bar":"bar","foo":"foo"}]

預期:

{"foo":"foo"}
{"bar":"bar","foo":"foo"}
[{"foo":"foo"}]
[{"bar":"bar","foo":"foo"}]

這是由於Gson默認情況下如何序列化集合。

為什么會這樣?

如果您不在乎原因,請滾動到底部,只需要修復即可。

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

在這種情況下,第三個優先級是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;
}

不使用CollectionTypeAdapterFactory ,不存在此包裝器,這就是為什么在前兩個示例中不發生這種包裝的原因。

tl; dr好吧,我該如何解決?

解決此問題的唯一方法是注冊自定義序列化程序。 在您的用例中,為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();
  }
}

然后,如果您這樣做:

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()));
}

您將獲得預期的輸出:

{"foo":"foo"}
{"bar":"bar","foo":"foo"}
[{"foo":"foo"}]
[{"bar":"bar","foo":"foo"}]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM