2015-06-03 19 views
10

L'esempio seguente mostra una classe (Club) che contiene una raccolta di una classe astratta (Membro). Sono confuso se ho bisogno di un TypeAdapter o JsonDeserializer per far funzionare correttamente la Deserializzazione. La serializzazione funziona bene senza alcun aiuto, ma la deserializzazione genera eccezioni. Per illustrare ho costruito il seguente test "clone". Se qualcuno potesse mostrare un esempio funzionante, sarei molto grato.Adattatore tipo Gson vs. Deseralizzatore personalizzato

Prima Classe Club

package gson.test; 
import java.util.ArrayList; 

import com.google.gson.Gson; 

public class Club { 
    public static void main(String[] 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(); 
     String 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 String title = "MyClub"; 
    private ArrayList<Member> members = new ArrayList<Member>(); 

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

Ora la classe di base astratta Stati

package gson.test; 
public abstract class Member { 
    private int type; 
    private String 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);} 
} 

E due concreti sottoclassi di Stati (oro e argento)

package gson.test; 
public class Gold extends Member { 
    private String 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 String silverData = "SomeSilverData"; 
    public Silver() { 
     super(); 
     this.setType(1); 
    } 
    public boolean equals(Silver that) { 
     return (super.equals(that) && this.silverData.equals(that.silverData)); 
    } 
} 

E infine la uscita

{"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) 
... 
+1

equals() il metodo deve utilizzare il tipo di parametro 'Object', non' Club' –

+0

Grazie, mi ci sarebbe voluto un po 'per capirlo. –

+0

Capito, grazie, voterò la prossima volta. –

risposta

13

Puoi fare entrambe le cose. Quello che scegli dipende in realtà dal potenziale impatto delle prestazioni e dalla quantità di codice che è disposto a scrivere.

I deserializzatori sono più costosi. Questo perché l'input per il deserializzatore è un albero json e GSon dovrà creare un sottoalbero JsonElement completo della proprietà corrispondente alla classe, prima che possa passarlo al deserializzatore. Se le tue classi hanno un sacco di nidificazione, il costo aumenta. Per gli oggetti semplici, sarà trascurabile.

Sembra che saprete quale classe creare in base al valore della proprietà type che verrà inclusa nell'oggetto di destinazione. Il tuo deserializzatore dovrà

  • sguardo al JsonElement oggetto passato, leggere la proprietà type, determinare il tipo
  • chiamata context.deserialize() con la classe e lo stesso elemento che è stato passato a voi
  • gettare un errore se il tipo era mancante o non valido

L'adattatore del tipo dovrà essere più complesso. L'input per il type adapter è stream, non un elemento/sottoalbero. Puoi caricare il valore successivo interamente dal flusso, analizzarlo e quindi fare esattamente ciò che ha fatto il deserializzatore, il che non ha senso e puoi semplicemente usare l'interfaccia del deserializzatore. In alternativa, puoi leggere lo stream, vedere quali proprietà ci sono, salvarle in variabili locali, fino ad arrivare alla proprietà type (non puoi prevedere la sua posizione), quindi finire di leggere il resto delle proprietà e creare il tuo finale Gold/Silver oggetti basati sul tipo e tutte le proprietà lette e salvate.

+0

Mi sono sottoposto a test, la mia serializzazione non funziona come pensavo, quindi mi sembra di aver bisogno di un serializzatore speciale per immergermi nei sottotipi della raccolta. Continuerò a testare e pubblicare un esempio di lavoro finale quando ne ho uno. –

8

Ok, vero esempio di lavoro (sono abbastanza sicuro questa volta).

The 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(String[] 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 
     String 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 String title = "MyClub"; 
    private ArrayList<Member> members = new ArrayList<Member>(); 

    public boolean equals(Object club) { 
     Club that = (Club) club; 
     if (!this.title.equals(that.title)) return false; 
     for (int i=0; i<this.members.size(); i++) { 
      Member member1 = this.getMember(i); 
      Member member2 = that.getMember(i); 
      if (! member1.equals(member2)) return false; 
     } 
     return true; 
    } 
    public void addMember(Member newMember) { members.add(newMember); } 
    public Member getMember(int i) { return members.get(i); } 
} 

Gli classe astratta

package gson.test; 
public abstract class Member { 
    private String clsname = this.getClass().getName() ; 
    private int type; 
    private String name = "unknown"; 

    public Member() { } 
    public Member(String 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); 
    } 
} 

il sotto-classi concrete Silver e Gold

package gson.test; 
public class Silver extends Member { 
    private String silverData = "SomeSilverData"; 
    public Silver() { 
     super(); 
     this.setType(1); 
    } 
    public Silver(String 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 String goldData = "SomeGoldData"; 
    private String extraData = "Extra Gold Data"; 
    public Gold() { 
     super(); 
     this.setType(2); 
    } 
    public Gold(String theName) { 
     super(theName); 
     this.setType(2); 
    } 
    public boolean equals(Gold that) { 
     Gold gold = (Gold) that; 
     return (super.equals(that) && this.goldData.equals(gold.goldData)); 
    } 
} 

Gli personalizzato Serailizer

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<Member> { 

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

L'usanza Deserializer

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<Member> { 
    @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; 
     } 
    } 
} 

E ... l'uscita

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" 
    } 
    ] 
} 

Vorrei sottolineare che il mio vero caso d'uso è quella in cui le prestazioni non dovrebbe essere un problema, sto caricando un cache di oggetti da jSon file di testo quindi la frequenza con questo codice viene eseguita rende le prestazioni molto meno importanti della manutenibilità.

Problemi correlati