Pourquoi Gson sérialise-t-il le type d’exécution dans la liste, pas le type à la compilation spécifié?

Pourquoi semble-t-il que Gson ignore la déclaration de type générique nestede lors de la sérialisation?

J’essaie d’obtenir que Gson utilise le type de compilation que je spécifie, au lieu du type d’exécution des objects de la liste. J’utilise également une super-classe abstraite pour A.java , mais l’exemple ci-dessous présente le même problème.

 public class A { public Ssortingng foo; } public class B extends A { public Ssortingng bar; } public static void main( Ssortingng[] args ) { Gson gson = new Gson(); B b = new B(); b.foo = "foo"; b.bar = "bar"; List list = new ArrayList(); list.add(b); System.out.println(gson.toJson(b, new TypeToken(){}.getType())); System.out.println(gson.toJson(b, new TypeToken(){}.getType())); System.out.println(gson.toJson(list, new TypeToken<List>(){}.getType())); System.out.println(gson.toJson(list, new TypeToken<List>(){}.getType())); } 

Sortie:

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

Attendu:

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

Ceci est dû à la façon dont Gson sérialise les collections par défaut.

Pourquoi cela arrive-t-il?

Faites défiler vers le bas si vous ne vous souciez pas de pourquoi et que vous voulez juste une solution.

Le paramètre par défaut CollectionTypeAdapterFactory Gson encapsule ses adaptateurs de type d’élément dans un élément appelé TypeAdapterRuntimeTypeWrapper . Lors du choix de l’adaptateur approprié, il utilise les priorités suivantes :

 // 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 

Dans ce cas, la troisième préférence est un adaptateur pour B , et la quasortingème préférence est un adaptateur pour A. Cela n’est pas évitable lorsque vous utilisez les sérialiseurs par défaut, car CollectionTypeAdapterFactory ne contient aucune condition :

 public Adapter(Gson context, Type elementType, TypeAdapter elementTypeAdapter, ObjectConstructor> constructor) { this.elementTypeAdapter = new TypeAdapterRuntimeTypeWrapper(context, elementTypeAdapter, elementType); this.constructor = constructor; } 

Ce wrapper n’est pas présent lorsque vous n’utilisez pas CollectionTypeAdapterFactory , raison pour laquelle cela ne se produit pas dans vos deux premiers exemples.

Ok, alors comment puis-je résoudre ce problème?

La seule façon de contourner ce problème consiste à enregistrer un sérialiseur personnalisé. Écrire un pour A fera l’affaire, dans votre cas d’utilisation:

 public class ATypeAdapter extends TypeAdapter { public A read(JsonReader reader) throws IOException { if (reader.peek() == JsonToken.NULL) { reader.nextNull(); return null; } reader.beginObject(); Ssortingng name = reader.nextName(); if(!"foo".equals(name)) throw new JsonSyntaxException("Expected field named foo"); A a = new A(); a.foo = reader.nextSsortingng(); 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(); } } 

Ensuite, si vous le faites:

 public static void main( Ssortingng[] args ) { GsonBuilder builder = new GsonBuilder(); builder.registerTypeAdapter(new TypeToken(){}.getType(), new ATypeAdapter()); Gson gson = builder.create(); B b = new B(); b.foo = "foo"; b.bar = "bar"; List list = new ArrayList(); list.add(b); System.out.println(gson.toJson(b, new TypeToken(){}.getType())); System.out.println(gson.toJson(b, new TypeToken(){}.getType())); System.out.println(gson.toJson(list, new TypeToken>(){}.getType())); System.out.println(gson.toJson(list, new TypeToken>(){}.getType())); } 

Vous obtenez le résultat attendu:

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