2013-04-01 21 views
20

Mam klasy takich jak:Jak radzić sobie z deserializacją z polimorfizmem?

public class Barn { 
    String type; 
    Animal animal; 
} 

public class Horse extends Animal { 
} 

public class Cow extends Animal { 
} 

i chcę serializacji ich listę:

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

Kiedy serializacji, informacja o typie zostaną utracone dla atrybutu Zwierząt. Czy istnieje sposób, w jaki mógłbym zainstalować odbiornik analizatora składni, aby zapewnić odpowiednią klasę do deserializacji, tak jak w przypadku napotkania każdego elementu na liście? Taki był pomysł ręcznego dostarczania łańcucha opisującego typ klasy.

Dzięki

Odpowiedz

15

W bazie kodu projekt Gson jest the RuntimeTypeAdapter, który podobno działa dobrze dla polimorficznych serializacji i deserializacji. Nie sądzę, żebym próbował go użyć. Aby uzyskać więcej informacji, patrz http://code.google.com/p/google-gson/issues/detail?id=231. Uwaga, nie została jeszcze uwzględniona w żadnych wydaniach Gson.

Jeśli korzystanie z niego nie odpowiada Twoim potrzebom, konieczne jest niestandardowe przetwarzanie deserializacji. Oto jedno z takich podejść, zakładając, że chcesz użyć zademonstrowanej struktury JSON. (Chciałbym podjąć nieco inne podejście, jeśli struktura JSON może być różny.)

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

Mogło to działać, jeśli kod podtypów (konia , krowa) są w różnych projektach Maven niż Barn? Mam cykliczną zależność z deserializacją Jacksona i próbuję znaleźć rozwiązanie – cyberjoac

6

Można użyć Gson Fire do tego. Kod będzie wyglądał mniej więcej tak:

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(); 
Powiązane problemy