Adaptateur de type Gson vs désaéraliseur personnalisé

L’exemple ci-dessous montre une classe (Club) contenant la collection d’une classe abstraite (Member). Je ne sais pas si j’ai besoin d’un TypeAdapter ou d’un JsonDeserializer pour que la désérialisation fonctionne correctement. La sérialisation fonctionne très bien sans aide, mais la désérialisation génère des exceptions. Pour illustrer j’ai construit le test “clone” suivant. Si quelqu’un pouvait montrer un exemple concret, je lui en serais très reconnaissant.

Première classe de club

package gson.test; import java.util.ArrayList; import com.google.gson.Gson; public class Club { public static void main(Ssortingng[] args) { // Setup a Club with 2 members Club myClub = new Club(); myClub.addMember(new Silver()); myClub.addMember(new Gold()); // Serialize to JSON Gson gson = new Gson(); Ssortingng myJsonClub = gson.toJson(myClub); System.out.println(myJsonClub); // De-Serialize to Club Club myNewClub = gson.fromJson(myJsonClub, Club.class); System.out.println(myClub.equals(myNewClub) ? "Cloned!" : "Failed"); } private Ssortingng title = "MyClub"; private ArrayList members = new ArrayList(); public boolean equals(Club that) { if (!this.title.equals(that.title)) return false; for (int i=0; i<this.members.size(); i++) { if (! this.getMember(i).equals(that.getMember(i))) return false; } return true; } public void addMember(Member newMember) { members.add(newMember); } public Member getMember(int i) { return members.get(i); } } 

Maintenant le membre de la classe de base abstrait

 package gson.test; public abstract class Member { private int type; private Ssortingng name = ""; public int getType() { return type; } public void setType(int type) { this.type = type; } public boolean equals(Member that) {return this.name.equals(that.name);} } 

Et deux sous-classes concrètes de Member (Gold et Silver)

 package gson.test; public class Gold extends Member { private Ssortingng goldData = "SomeGoldData"; public Gold() { super(); this.setType(2); } public boolean equals(Gold that) { return (super.equals(that) && this.goldData.equals(that.goldData)); } } package gson.test; public class Silver extends Member { private Ssortingng silverData = "SomeSilverData"; public Silver() { super(); this.setType(1); } public boolean equals(Silver that) { return (super.equals(that) && this.silverData.equals(that.silverData)); } } 

Et enfin la sortie

  {"title":"MyClub","members":[{"silverData":"SomeSilverData","type":1,"name":""},{"goldData":"SomeGoldData","type":2,"name":""}]} Exception in thread "main" java.lang.RuntimeException: Failed to invoke public gson.test.Member() with no args at com.google.gson.internal.ConstructorConstructor$3.construct(ConstructorConstructor.java:107) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:186) ... 

Vous pouvez faire les deux. Le choix que vous choisissez dépend vraiment de l’impact potentiel sur les performances et de la quantité de code que vous êtes prêt à écrire.

Les désérialiseurs sont plus chers. En effet, l’entrée du désérialiseur est un arbre json et GSon doit créer un sous-arbre JsonElement complet de la propriété qui correspond à votre classe avant de pouvoir le transmettre à votre désérialiseur. Si vos classes ont beaucoup d’imbrication, le coût augmente. Pour les objects simples, ce sera négligeable.

Il semble que vous sachiez quelle classe créer en fonction de la valeur de la propriété type qui sera incluse dans l’object cible. Votre désérialiseur devra

  • examinez l’object JsonElement passé, lisez la propriété type , déterminez le type
  • appelez context.deserialize() avec la classe et le même élément qui vous a été transmis
  • renvoie une erreur si le type était manquant ou invalide

Votre adaptateur de type devra être plus complexe. L’entrée de l’adaptateur de type est un stream, pas un élément / sous-arbre. Vous pouvez charger la valeur suivante entièrement à partir du stream, l’parsingr, puis faire exactement ce que fait le désérialiseur, ce qui n’a aucun sens et vous pouvez simplement utiliser l’interface de désérialiseur. Vous pouvez également lire le stream, voir quelles sont les propriétés existantes, les enregistrer dans des variables locales jusqu’à obtenir la propriété type (vous ne pouvez pas prédire son emplacement), puis terminer la lecture du rest des propriétés et créer votre dernière Gold objects Gold / Silver basés sur le type et toutes les propriétés lues et enregistrées.

Ok, exemple de travail réel (je suis à peu près sûr cette fois).

Le club

 package gson.test; import java.util.ArrayList; import com.google.gson.Gson; import com.google.gson.GsonBuilder; public class Club { public static void main(Ssortingng[] args) { // Setup a Club with 2 members Club myClub = new Club(); myClub.addMember(new Silver("Jack")); myClub.addMember(new Gold("Jill")); myClub.addMember(new Silver("Mike")); // Get the GSON Object and register Type Adapter GsonBuilder builder = new GsonBuilder(); builder.registerTypeAdapter(Member.class, new MemberDeserializer()); builder.registerTypeAdapter(Member.class, new MemberSerializer()); builder.setPrettyPrinting(); Gson gson = builder.create(); // Serialize Club to JSON Ssortingng myJsonClub = gson.toJson(myClub); // De-Serialize to Club Club myNewClub = gson.fromJson(myJsonClub, Club.class); System.out.println(myClub.equals(myNewClub) ? "Cloned!" : "Failed"); System.out.println(gson.toJson(myNewClub)); } private Ssortingng title = "MyClub"; private ArrayList members = new ArrayList(); public boolean equals(Object club) { Club that = (Club) club; if (!this.title.equals(that.title)) return false; for (int i=0; i 

La classe des membres abstraits

 package gson.test; public abstract class Member { private Ssortingng clsname = this.getClass().getName() ; private int type; private Ssortingng name = "unknown"; public Member() { } public Member(Ssortingng theName) {this.name = theName;} public int getType() { return type; } public void setType(int type) { this.type = type; } public boolean equals(Object member) { Member that = (Member) member; return this.name.equals(that.name); } } 

Les sous-classes de béton Silver et Gold

 package gson.test; public class Silver extends Member { private Ssortingng silverData = "SomeSilverData"; public Silver() { super(); this.setType(1); } public Silver(Ssortingng theName) { super(theName); this.setType(1); } public boolean equals(Object that) { Silver silver = (Silver)that; return (super.equals(that) && this.silverData.equals(silver.silverData)); } } package gson.test; public class Gold extends Member { private Ssortingng goldData = "SomeGoldData"; private Ssortingng extraData = "Extra Gold Data"; public Gold() { super(); this.setType(2); } public Gold(Ssortingng theName) { super(theName); this.setType(2); } public boolean equals(Gold that) { Gold gold = (Gold) that; return (super.equals(that) && this.goldData.equals(gold.goldData)); } } 

Le serailizer de membre personnalisé

 package gson.test; import java.lang.reflect.Type; import com.google.gson.JsonElement; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; public class MemberSerializer implements JsonSerializer { public JsonElement serialize(Member src, Type member, JsonSerializationContext context) { switch (src.getType()) { case 1: return context.serialize((Silver)src); case 2: return context.serialize((Gold)src); default: return null; } } } 

Le désérialiseur personnalisé

 package gson.test; import java.lang.reflect.Type; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; public class MemberDeserializer implements JsonDeserializer { @Override public Member deserialize(JsonElement json, Type member, JsonDeserializationContext context) { int myType = json.getAsJsonObject().get("type").getAsInt(); switch (myType) { case 1: return context.deserialize(json, Silver.class); case 2: return context.deserialize(json, Gold.class); default: return null; } } } 

Et ... la sortie

 Cloned! { "title": "MyClub", "members": [ { "silverData": "SomeSilverData", "clsname": "gson.test.Silver", "type": 1, "name": "Jack" }, { "goldData": "SomeGoldData", "extraData": "Extra Gold Data", "clsname": "gson.test.Gold", "type": 2, "name": "Jill" }, { "silverData": "SomeSilverData", "clsname": "gson.test.Silver", "type": 1, "name": "Mike" } ] } 

Je dois noter que mon cas d'utilisation réel est celui où les performances ne devraient pas être un problème, je charge un cache d'objects à partir de fichiers texte jSon, de sorte que la fréquence d'exécution de ce code rend les performances beaucoup moins importantes que la maintenabilité.