2013-04-01 16 views
20

Ho una classe come:Come gestire la deserializzazione con il polimorfismo?

public class Barn { 
    String type; 
    Animal animal; 
} 

public class Horse extends Animal { 
} 

public class Cow extends Animal { 
} 

e voglio serializzare un elenco di loro:

List<Barn> barns = new ArrayList<Barn>(); 

Barn barn1 = new Barn(); 
barn1.setType("horse"); 
barn1.setAnimal(new Horse()); 
barns.add(barn1); 

Barn barn2 = new Barn(); 
barn2.setType("cow"); 
barn2.setAnimal(new Cow()); 
barns.add(barn2); 

... 

Group<Barn> barns = gson.fromJson(serialized); 

Quando ho serializzare, informazioni sul tipo sarà perso per l'attributo degli animali. C'è un modo per installare un parser listener in qualche modo in modo da poter fornire la classe giusta per deserializzare come quando si incontra ogni elemento della lista? Questa era l'idea alla base della fornitura manuale di una stringa che descriveva il tipo di classe.

Grazie

risposta

15

Nella base di codice del progetto è GSON the RuntimeTypeAdapter, che funziona come riferito bene per la serializzazione polimorfico e deserializzazione. Non penso di aver ancora provato a usarlo. Vedi http://code.google.com/p/google-gson/issues/detail?id=231 per maggiori informazioni. Nota, non è ancora stato incluso in nessuna versione di Gson.

Se l'utilizzo non soddisfa le proprie esigenze, è necessario elaborare la deserializzazione personalizzata. Di seguito è riportato un approccio di questo tipo, assumendo che si desideri utilizzare la struttura JSON dimostrata. (Mi piacerebbe prendere un approccio un po 'diverso, se la struttura JSON potrebbe essere diverso.)

import java.lang.reflect.Type; 
import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 

import com.google.gson.Gson; 
import com.google.gson.GsonBuilder; 
import com.google.gson.JsonDeserializationContext; 
import com.google.gson.JsonDeserializer; 
import com.google.gson.JsonElement; 
import com.google.gson.JsonObject; 
import com.google.gson.JsonParseException; 
import com.google.gson.reflect.TypeToken; 

public class App 
{ 
    public static void main(String[] args) 
    { 
    Barn[] barns = {new Barn(), new Barn()}; 
    barns[0].type = "horse"; 
    barns[0].animal = new Horse(); 
    barns[1].type = "cow"; 
    barns[1].animal = new Cow(); 

    String json = new Gson().toJson(barns); 
    // [{"type":"horse","animal":{}},{"type":"cow","animal":{}}] 

    BarnDeserializer deserializer = new BarnDeserializer("type"); 
    deserializer.registerBarnType("horse", Horse.class); 
    deserializer.registerBarnType("cow", Cow.class); 
    Gson gson = new GsonBuilder().registerTypeAdapter(Barn.class, deserializer).create(); 

    List<Barn> barns2= gson.fromJson(json, new TypeToken<List<Barn>>(){}.getType()); 
    for (Barn barn : barns2) 
    { 
     System.out.println(barn.animal.getClass()); 
    } 
    } 
} 

class BarnDeserializer implements JsonDeserializer<Barn> 
{ 
    String barnTypeElementName; 
    Gson gson; 
    Map<String, Class<? extends Animal>> barnTypeRegistry; 

    BarnDeserializer(String barnTypeElementName) 
    { 
    this.barnTypeElementName = barnTypeElementName; 
    gson = new Gson(); 
    barnTypeRegistry = new HashMap<>(); // Java 7 required for this syntax. 
    } 

    void registerBarnType(String barnTypeName, Class<? extends Animal> animalType) 
    { 
    barnTypeRegistry.put(barnTypeName, animalType); 
    } 

    @Override 
    public Barn deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) 
     throws JsonParseException 
    { 
    JsonObject barnObject = json.getAsJsonObject(); 
    JsonElement animalTypeElement = barnObject.get(barnTypeElementName); 
    Barn barn = new Barn(); 
    barn.type = animalTypeElement.getAsString(); 
    Class<? extends Animal> animalType = barnTypeRegistry.get(barn.type); 
    barn.animal = gson.fromJson(barnObject.get("animal"), animalType); 
    return barn; 
    } 
} 

class Barn {String type; Animal animal;} 
class Animal {} 
class Horse extends Animal {} 
class Cow extends Animal {} 
+0

Potrebbe questo codice di lavoro se i sottotipi (cavallo , mucca) sono in diversi progetti Maven rispetto a Barn? Ho una dipendenza circolare con la deserializzazione di Jackson e sto cercando di trovare una soluzione – cyberjoac

6

si potrebbe usare Gson Fire per questo. Il codice dovrebbe essere simile a questa:

GsonFireBuilder builder = new GsonFireBuilder() 
    .registerTypeSelector(Barn.class, new TypeSelector<Barn>() { 
     @Override 
     public Class<? extends Barn> getClassForElement(JsonElement readElement) { 
      String type = readElement.getAsJsonObject().get("type").getAsString(); 
      if(type.equals("horse")){ 
       return Horse.class; 
      } else if(type.equals("cow")) { 
       return Cow.class; 
      } else { 
       return null; //returning null will trigger Gson's default behavior 
      } 
     } 
    }); 
Gson gson = builder.createGson(); 
Problemi correlati