Polymorphismus mit gson


102

Ich habe ein Problem beim Deserialisieren eines JSON-Strings mit Gson. Ich erhalte eine Reihe von Befehlen. Der Befehl kann Start, Stopp oder eine andere Art von Befehl sein. Natürlich habe ich Polymorphismus und Start / Stopp-Befehl erbt von Befehl.

Wie kann ich es mit gson zurück zum richtigen Befehlsobjekt serialisieren?

Scheint, dass ich nur den Basistyp bekomme, das ist der deklarierte Typ und niemals der Laufzeittyp.


Antworten:


120

Das ist etwas spät, aber ich musste heute genau das Gleiche tun. Basierend auf meinen Recherchen und wenn Sie gson-2.0 verwenden, möchten Sie wirklich nicht die registerTypeHierarchyAdapter- Methode verwenden, sondern den profaneren registerTypeAdapter . Und Sie müssen sicherlich keine Instanzen von Instanzen erstellen oder Adapter für die abgeleiteten Klassen schreiben: nur einen Adapter für die Basisklasse oder Schnittstelle, vorausgesetzt natürlich, Sie sind mit der Standardserialisierung der abgeleiteten Klassen zufrieden. Wie auch immer, hier ist der Code (Paket und Importe entfernt) (auch in Github verfügbar ):

Die Basisklasse (Schnittstelle in meinem Fall):

public interface IAnimal { public String sound(); }

Die zwei abgeleiteten Klassen, Cat:

public class Cat implements IAnimal {

    public String name;

    public Cat(String name) {
        super();
        this.name = name;
    }

    @Override
    public String sound() {
        return name + " : \"meaow\"";
    };
}

Und Hund:

public class Dog implements IAnimal {

    public String name;
    public int ferocity;

    public Dog(String name, int ferocity) {
        super();
        this.name = name;
        this.ferocity = ferocity;
    }

    @Override
    public String sound() {
        return name + " : \"bark\" (ferocity level:" + ferocity + ")";
    }
}

Der IAnimalAdapter:

public class IAnimalAdapter implements JsonSerializer<IAnimal>, JsonDeserializer<IAnimal>{

    private static final String CLASSNAME = "CLASSNAME";
    private static final String INSTANCE  = "INSTANCE";

    @Override
    public JsonElement serialize(IAnimal src, Type typeOfSrc,
            JsonSerializationContext context) {

        JsonObject retValue = new JsonObject();
        String className = src.getClass().getName();
        retValue.addProperty(CLASSNAME, className);
        JsonElement elem = context.serialize(src); 
        retValue.add(INSTANCE, elem);
        return retValue;
    }

    @Override
    public IAnimal deserialize(JsonElement json, Type typeOfT,
            JsonDeserializationContext context) throws JsonParseException  {
        JsonObject jsonObject = json.getAsJsonObject();
        JsonPrimitive prim = (JsonPrimitive) jsonObject.get(CLASSNAME);
        String className = prim.getAsString();

        Class<?> klass = null;
        try {
            klass = Class.forName(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new JsonParseException(e.getMessage());
        }
        return context.deserialize(jsonObject.get(INSTANCE), klass);
    }
}

Und die Testklasse:

public class Test {

    public static void main(String[] args) {
        IAnimal animals[] = new IAnimal[]{new Cat("Kitty"), new Dog("Brutus", 5)};
        Gson gsonExt = null;
        {
            GsonBuilder builder = new GsonBuilder();
            builder.registerTypeAdapter(IAnimal.class, new IAnimalAdapter());
            gsonExt = builder.create();
        }
        for (IAnimal animal : animals) {
            String animalJson = gsonExt.toJson(animal, IAnimal.class);
            System.out.println("serialized with the custom serializer:" + animalJson);
            IAnimal animal2 = gsonExt.fromJson(animalJson, IAnimal.class);
            System.out.println(animal2.sound());
        }
    }
}

Wenn Sie Test :: main ausführen, erhalten Sie die folgende Ausgabe:

serialized with the custom serializer:
{"CLASSNAME":"com.synelixis.caches.viz.json.playground.plainAdapter.Cat","INSTANCE":{"name":"Kitty"}}
Kitty : "meaow"
serialized with the custom serializer:
{"CLASSNAME":"com.synelixis.caches.viz.json.playground.plainAdapter.Dog","INSTANCE":{"name":"Brutus","ferocity":5}}
Brutus : "bark" (ferocity level:5)

Ich habe das oben Genannte tatsächlich auch mit der registerTypeHierarchyAdapter- Methode durchgeführt, aber dafür mussten anscheinend benutzerdefinierte DogAdapter- und CatAdapter-Serializer- / Deserializer-Klassen implementiert werden, die jedes Mal, wenn Sie Dog oder Cat ein weiteres Feld hinzufügen möchten, schwierig zu pflegen sind.


5
Beachten Sie, dass die Serialisierung von Klassennamen und die Deserialisierung (aus Benutzereingaben) mithilfe von Class.forName in einigen Situationen Auswirkungen auf die Sicherheit haben kann und daher vom Gson-Entwicklerteam nicht empfohlen wird. code.google.com/p/google-gson/issues/detail?id=340#c2
Programmierer Bruce

4
Wie haben Sie es geschafft, keine Endlosschleife bei der Serialisierung zu erhalten? Sie rufen context.serialize (src) auf. Dadurch wird Ihr Adapter erneut aufgerufen. Dies ist, was in meinem ähnlichen Code passiert ist.
Che Javara

6
Falsch. Diese Lösung funktioniert nicht. Wenn Sie context.serialize auf irgendeine Weise aufrufen, erhalten Sie eine unendliche Rekursion. Ich frage mich, warum Leute posten, ohne Code zu testen. Ich habe es mit 2.2.1 versucht. Siehe den Fehler in stackoverflow.com/questions/13244769/…
che javara

4
@MarcusJuniusBrutus Ich habe Ihren Code ausgeführt und es scheint, dass er nur in diesem speziellen Fall funktioniert - weil Sie eine Superschnittstelle IAnimal definiert haben und IAnimalAdapter sie verwendet. Wenn Sie stattdessen nur 'Katze' hatten, erhalten Sie das Problem der unendlichen Rekursion. Daher funktioniert diese Lösung im allgemeinen Fall immer noch nicht - nur wenn Sie eine gemeinsame Schnittstelle definieren können. In meinem Fall gab es keine Schnittstelle, daher musste ich mit TypeAdapterFactory einen anderen Ansatz verwenden.
Che Javara

2
Benutzer src.getClass (). GetName () anstelle von src.getClass (). GetCanonicalName (). Dadurch funktioniert der Code auch für innere / verschachtelte Klassen.
mR_fr0g

13

Gson verfügt derzeit über einen Mechanismus zum Registrieren eines Typhierarchie- Adapters , der Berichten zufolge für eine einfache polymorphe Deserialisierung konfiguriert werden kann. Ich sehe jedoch nicht, wie dies der Fall ist, da ein Typhierarchie-Adapter nur ein kombinierter Serializer / Deserializer / Instanz-Ersteller zu sein scheint. Überlassen Sie die Details der Instanzerstellung dem Codierer, ohne eine tatsächliche polymorphe Typregistrierung bereitzustellen.

Es sieht so aus, als ob Gson bald die RuntimeTypeAdapterfür eine einfachere polymorphe Deserialisierung haben wird. Weitere Informationen finden Sie unter http://code.google.com/p/google-gson/issues/detail?id=231 .

Wenn die Verwendung des neuen RuntimeTypeAdapternicht möglich ist und Sie Gson verwenden müssen, müssen Sie wahrscheinlich Ihre eigene Lösung entwickeln und einen benutzerdefinierten Deserializer entweder als Typhierarchie-Adapter oder als Typ-Adapter registrieren. Das Folgende ist ein solches Beispiel.

// output:
//     Starting machine1
//     Stopping machine2

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

import com.google.gson.FieldNamingPolicy;
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;

public class Foo
{
  // [{"machine_name":"machine1","command":"start"},{"machine_name":"machine2","command":"stop"}]
  static String jsonInput = "[{\"machine_name\":\"machine1\",\"command\":\"start\"},{\"machine_name\":\"machine2\",\"command\":\"stop\"}]";

  public static void main(String[] args)
  {
    GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
    CommandDeserializer deserializer = new CommandDeserializer("command");
    deserializer.registerCommand("start", Start.class);
    deserializer.registerCommand("stop", Stop.class);
    gsonBuilder.registerTypeAdapter(Command.class, deserializer);
    Gson gson = gsonBuilder.create();
    Command[] commands = gson.fromJson(jsonInput, Command[].class);
    for (Command command : commands)
    {
      command.execute();
    }
  }
}

class CommandDeserializer implements JsonDeserializer<Command>
{
  String commandElementName;
  Gson gson;
  Map<String, Class<? extends Command>> commandRegistry;

  CommandDeserializer(String commandElementName)
  {
    this.commandElementName = commandElementName;
    GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
    gson = gsonBuilder.create();
    commandRegistry = new HashMap<String, Class<? extends Command>>();
  }

  void registerCommand(String command, Class<? extends Command> commandInstanceClass)
  {
    commandRegistry.put(command, commandInstanceClass);
  }

  @Override
  public Command deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
      throws JsonParseException
  {
    try
    {
      JsonObject commandObject = json.getAsJsonObject();
      JsonElement commandTypeElement = commandObject.get(commandElementName);
      Class<? extends Command> commandInstanceClass = commandRegistry.get(commandTypeElement.getAsString());
      Command command = gson.fromJson(json, commandInstanceClass);
      return command;
    }
    catch (Exception e)
    {
      throw new RuntimeException(e);
    }
  }
}

abstract class Command
{
  String machineName;

  Command(String machineName)
  {
    this.machineName = machineName;
  }

  abstract void execute();
}

class Stop extends Command
{
  Stop(String machineName)
  {
    super(machineName);
  }

  void execute()
  {
    System.out.println("Stopping " + machineName);
  }
}

class Start extends Command
{
  Start(String machineName)
  {
    super(machineName);
  }

  void execute()
  {
    System.out.println("Starting " + machineName);
  }
}

Wenn Sie APIs ändern können, beachten Sie, dass Jackson derzeit über einen Mechanismus für eine relativ einfache polymorphe Deserialisierung verfügt. Ich habe einige Beispiele unter programmerbruce.blogspot.com/2011/05/…
Programmer Bruce

RuntimeTypeAdapterist jetzt fertig, leider sieht es noch nicht so aus, als wäre es im Gson-Kern. :-(
Jonathan

8

Marcus Junius Brutus hatte eine großartige Antwort (danke!). Um sein Beispiel zu erweitern, können Sie seine Adapterklasse generisch so gestalten, dass sie für alle Objekttypen (nicht nur für IAnimal) mit den folgenden Änderungen funktioniert:

class InheritanceAdapter<T> implements JsonSerializer<T>, JsonDeserializer<T>
{
....
    public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context)
....
    public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
....
}

Und in der Testklasse:

public class Test {
    public static void main(String[] args) {
        ....
            builder.registerTypeAdapter(IAnimal.class, new InheritanceAdapter<IAnimal>());
        ....
}

1
Nachdem ich seine Lösung implementiert hatte, war mein nächster Gedanke, genau dies zu tun :-)
David Levy

7

GSON hat hier einen ziemlich guten Testfall, der zeigt, wie ein Typhierarchie-Adapter definiert und registriert wird.

http://code.google.com/p/google-gson/source/browse/trunk/gson/src/test/java/com/google/gson/functional/TypeHierarchyAdapterTest.java?r=739

Um dies zu verwenden, gehen Sie wie folgt vor:

    gson = new GsonBuilder()
          .registerTypeAdapter(BaseQuestion.class, new BaseQuestionAdaptor())
          .create();

Die Serialisierungsmethode des Adapters kann eine kaskadierende if-else-Überprüfung des Serialisierungstyps sein.

    JsonElement result = new JsonObject();

    if (src instanceof SliderQuestion) {
        result = context.serialize(src, SliderQuestion.class);
    }
    else if (src instanceof TextQuestion) {
        result = context.serialize(src, TextQuestion.class);
    }
    else if (src instanceof ChoiceQuestion) {
        result = context.serialize(src, ChoiceQuestion.class);
    }

    return result;

Deserialisieren ist ein bisschen hacky. Im Unit-Test-Beispiel wird geprüft, ob verräterische Attribute vorhanden sind, um zu entscheiden, für welche Klasse dererialisiert werden soll. Wenn Sie die Quelle des zu serialisierenden Objekts ändern können, können Sie jeder Instanz ein Attribut 'classType' hinzufügen, das den FQN des Namens der Instanzklasse enthält. Dies ist jedoch sehr unobjektorientiert.


4

Google hat eine eigene RuntimeTypeAdapterFactory veröffentlicht , um den Polymorphismus zu behandeln. Leider ist sie nicht Teil des Gson-Kerns (Sie müssen die Klasse kopieren und in Ihr Projekt einfügen).

Beispiel:

RuntimeTypeAdapterFactory<Animal> runtimeTypeAdapterFactory = RuntimeTypeAdapterFactory
.of(Animal.class, "type")
.registerSubtype(Dog.class, "dog")
.registerSubtype(Cat.class, "cat");

Gson gson = new GsonBuilder()
    .registerTypeAdapterFactory(runtimeTypeAdapterFactory)
    .create();

Hier habe ich ein voll funktionsfähiges Beispiel mit den Modellen Animal, Dog und Cat veröffentlicht.

Ich denke, es ist besser, sich auf diesen Adapter zu verlassen, als ihn von Grund auf neu zu implementieren.


2

Lange Zeit ist vergangen, aber ich konnte online keine wirklich gute Lösung finden. Hier ist eine kleine Wendung der Lösung von @ MarcusJuniusBrutus, die die unendliche Rekursion vermeidet.

Behalten Sie den gleichen Deserializer bei, entfernen Sie jedoch den Serializer -

public class IAnimalAdapter implements JsonDeSerializer<IAnimal> {
  private static final String CLASSNAME = "CLASSNAME";
  private static final String INSTANCE  = "INSTANCE";

  @Override
  public IAnimal deserialize(JsonElement json, Type typeOfT,
        JsonDeserializationContext context) throws JsonParseException  {
    JsonObject jsonObject =  json.getAsJsonObject();
    JsonPrimitive prim = (JsonPrimitive) jsonObject.get(CLASSNAME);
    String className = prim.getAsString();

    Class<?> klass = null;
    try {
        klass = Class.forName(className);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
        throw new JsonParseException(e.getMessage());
    }
    return context.deserialize(jsonObject.get(INSTANCE), klass);
  }
}

Fügen Sie dann in Ihrer ursprünglichen Klasse ein Feld mit hinzu @SerializedName("CLASSNAME"). Der Trick besteht nun darin, dies im Konstruktor der Basisklasse zu initialisieren. Machen Sie Ihre Schnittstelle also zu einer abstrakten Klasse.

public abstract class IAnimal {
  @SerializedName("CLASSNAME")
  public String className;

  public IAnimal(...) {
    ...
    className = this.getClass().getName();
  }
}

Der Grund, warum es hier keine unendliche Rekursion gibt, ist, dass wir die eigentliche Laufzeitklasse (dh Dog not IAnimal) an übergeben context.deserialize. Dies wird unseren Typadapter nicht aufrufen, solange wir ihn verwenden registerTypeAdapterund nichtregisterTypeHierarchyAdapter


2

Aktualisierte Antwort - Beste Teile aller anderen Antworten

Ich beschreibe Lösungen für verschiedene Anwendungsfälle und würde auch das Problem der unendlichen Rekursion ansprechen

  • Fall 1: Sie sind in der Steuerung der Klassen , dh Sie erhalten eine eigene zu schreiben Cat, DogKlassen sowie die IAnimalSchnittstelle. Sie können einfach der Lösung von @ marcus-junius-brutus folgen (die am besten bewertete Antwort).

    Es wird keine unendliche Rekursion geben, wenn es eine gemeinsame Basisschnittstelle als gibt IAnimal

    Aber was ist, wenn ich die IAnimaloder eine solche Schnittstelle nicht implementieren möchte ?

    Dann erzeugt @ marcus-junius-brutus (die am besten bewertete Antwort) einen unendlichen Rekursionsfehler. In diesem Fall können wir etwas wie das Folgende tun.

    Wir müssten einen Kopierkonstruktor innerhalb der Basisklasse und eine Wrapper-Unterklasse wie folgt erstellen :

.

// Base class(modified)
public class Cat implements IAnimal {

    public String name;

    public Cat(String name) {
        super();
        this.name = name;
    }
    // COPY CONSTRUCTOR
    public Cat(Cat cat) {
        this.name = cat.name;
    }

    @Override
    public String sound() {
        return name + " : \"meaow\"";
    };
}



    // The wrapper subclass for serialization
public class CatWrapper extends Cat{


    public CatWrapper(String name) {
        super(name);
    }

    public CatWrapper(Cat cat) {
        super(cat);
    }
}

Und der Serializer für den Typ Cat:

public class CatSerializer implements JsonSerializer<Cat> {

    @Override
    public JsonElement serialize(Cat src, Type typeOfSrc, JsonSerializationContext context) {

        // Essentially the same as the type Cat
        JsonElement catWrapped = context.serialize(new CatWrapper(src));

        // Here, we can customize the generated JSON from the wrapper as we want.
        // We can add a field, remove a field, etc.


        return modifyJSON(catWrapped);
    }

    private JsonElement modifyJSON(JsonElement base){
        // TODO: Modify something
        return base;
    }
}

Warum also ein Kopierkonstruktor?

Sobald Sie den Kopierkonstruktor definiert haben, wird Ihr Wrapper unabhängig von der Änderung der Basisklasse dieselbe Rolle übernehmen. Zweitens, wenn wir keinen Kopierkonstruktor definieren und einfach die Basisklasse unterordnen, müssten wir in Bezug auf die erweiterte Klasse "sprechen", d CatWrapper. H. Es ist durchaus möglich, dass Ihre Komponenten in Bezug auf die Basisklasse und nicht auf den Wrapper-Typ sprechen.

Gibt es eine einfache Alternative?

Sicher, es wurde jetzt von Google eingeführt - dies ist die RuntimeTypeAdapterFactoryImplementierung:

RuntimeTypeAdapterFactory<Animal> runtimeTypeAdapterFactory = RuntimeTypeAdapterFactory
.of(Animal.class, "type")
.registerSubtype(Dog.class, "dog")
.registerSubtype(Cat.class, "cat");

Gson gson = new GsonBuilder()
    .registerTypeAdapterFactory(runtimeTypeAdapterFactory)
    .create();

Hier müssten Sie ein Feld namens "Typ" einfügen Animalund den Wert desselben im Inneren Dogals "Hund", Catals "Katze" angeben.

Vollständiges Beispiel: https://static.javadoc.io/org.danilopianini/gson-extras/0.2.1/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.html

  • Fall 2: Sie haben keine Kontrolle über die Klassen . Sie treten einer Firma bei oder verwenden eine Bibliothek, in der die Klassen bereits definiert sind und Ihr Manager nicht möchte, dass Sie sie in irgendeiner Weise ändern. Sie können Ihre Klassen in Unterklassen unterteilen und sie eine gemeinsame Markierungsschnittstelle implementieren lassen (die keine Methoden hat ) wie AnimalInterface.

    Ex:

.

// The class we are NOT allowed to modify

public class Dog implements IAnimal {

    public String name;
    public int ferocity;

    public Dog(String name, int ferocity) {
        super();
        this.name = name;
        this.ferocity = ferocity;
    }

    @Override
    public String sound() {
        return name + " : \"bark\" (ferocity level:" + ferocity + ")";
    }
}


// The marker interface

public interface AnimalInterface {
}

// The subclass for serialization

public class DogWrapper  extends Dog implements AnimalInterface{

    public DogWrapper(String name, int ferocity) {
        super(name, ferocity);
    }

}

// The subclass for serialization

public class CatWrapper extends Cat implements AnimalInterface{


    public CatWrapper(String name) {
        super(name);
    }
}

Also würden wir CatWrapperanstelle von Cat, DogWrapperanstelle von Dogund AlternativeAnimalAdapteranstelle von verwendenIAnimalAdapter

// The only difference between `IAnimalAdapter` and `AlternativeAnimalAdapter` is that of the interface, i.e, `AnimalInterface` instead of `IAnimal`

public class AlternativeAnimalAdapter implements JsonSerializer<AnimalInterface>, JsonDeserializer<AnimalInterface> {

    private static final String CLASSNAME = "CLASSNAME";
    private static final String INSTANCE  = "INSTANCE";

    @Override
    public JsonElement serialize(AnimalInterface src, Type typeOfSrc,
                                 JsonSerializationContext context) {

        JsonObject retValue = new JsonObject();
        String className = src.getClass().getName();
        retValue.addProperty(CLASSNAME, className);
        JsonElement elem = context.serialize(src); 
        retValue.add(INSTANCE, elem);
        return retValue;
    }

    @Override
    public AnimalInterface deserialize(JsonElement json, Type typeOfT,
            JsonDeserializationContext context) throws JsonParseException  {
        JsonObject jsonObject = json.getAsJsonObject();
        JsonPrimitive prim = (JsonPrimitive) jsonObject.get(CLASSNAME);
        String className = prim.getAsString();

        Class<?> klass = null;
        try {
            klass = Class.forName(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new JsonParseException(e.getMessage());
        }
        return context.deserialize(jsonObject.get(INSTANCE), klass);
    }
}

Wir führen einen Test durch:

public class Test {

    public static void main(String[] args) {

        // Note that we are using the extended classes instead of the base ones
        IAnimal animals[] = new IAnimal[]{new CatWrapper("Kitty"), new DogWrapper("Brutus", 5)};
        Gson gsonExt = null;
        {
            GsonBuilder builder = new GsonBuilder();
            builder.registerTypeAdapter(AnimalInterface.class, new AlternativeAnimalAdapter());
            gsonExt = builder.create();
        }
        for (IAnimal animal : animals) {
            String animalJson = gsonExt.toJson(animal, AnimalInterface.class);
            System.out.println("serialized with the custom serializer:" + animalJson);
            AnimalInterface animal2 = gsonExt.fromJson(animalJson, AnimalInterface.class);
        }
    }
}

Ausgabe:

serialized with the custom serializer:{"CLASSNAME":"com.examples_so.CatWrapper","INSTANCE":{"name":"Kitty"}}
serialized with the custom serializer:{"CLASSNAME":"com.examples_so.DogWrapper","INSTANCE":{"name":"Brutus","ferocity":5}}

1

Wenn Sie einen TypeAdapter für einen Typ und einen anderen für seinen Untertyp verwalten möchten, können Sie eine TypeAdapterFactory wie folgt verwenden:

public class InheritanceTypeAdapterFactory implements TypeAdapterFactory {

    private Map<Class<?>, TypeAdapter<?>> adapters = new LinkedHashMap<>();

    {
        adapters.put(Animal.class, new AnimalTypeAdapter());
        adapters.put(Dog.class, new DogTypeAdapter());
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
        TypeAdapter<T> typeAdapter = null;
        Class<?> currentType = Object.class;
        for (Class<?> type : adapters.keySet()) {
            if (type.isAssignableFrom(typeToken.getRawType())) {
                if (currentType.isAssignableFrom(type)) {
                    currentType = type;
                    typeAdapter = (TypeAdapter<T>)adapters.get(type);
                }
            }
        }
        return typeAdapter;
    }
}

Diese Fabrik sendet den genauesten TypeAdapter


0

Wenn Sie die Antwort von Marcus Junius Brutus mit der Bearbeitung von user2242263 kombinieren, können Sie vermeiden, dass Sie in Ihrem Adapter eine große Klassenhierarchie angeben müssen, indem Sie Ihren Adapter so definieren, dass er an einem Schnittstellentyp arbeitet. Sie können dann Standardimplementierungen von toJSON () und fromJSON () in Ihrer Schnittstelle bereitstellen (die nur diese beiden Methoden enthält) und jede Klasse, die Sie zur Serialisierung benötigen, Ihre Schnittstelle implementieren lassen. Um das Casting zu behandeln, können Sie in Ihren Unterklassen eine statische fromJSON () -Methode bereitstellen, die das entsprechende Casting von Ihrem Schnittstellentyp deserialisiert und ausführt. Dies hat bei mir hervorragend funktioniert (seien Sie vorsichtig beim Serialisieren / Deserialisieren von Klassen, die Hashmaps enthalten - fügen Sie dies hinzu, wenn Sie Ihren gson-Builder instanziieren:

GsonBuilder builder = new GsonBuilder().enableComplexMapKeySerialization();

Hoffe, das hilft jemandem, Zeit und Mühe zu sparen!

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.