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
, Dog
Klassen sowie die IAnimal
Schnittstelle. 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 IAnimal
oder 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 RuntimeTypeAdapterFactory
Implementierung:
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 Animal
und den Wert desselben im Inneren Dog
als "Hund", Cat
als "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 CatWrapper
anstelle von Cat
, DogWrapper
anstelle von Dog
und
AlternativeAnimalAdapter
anstelle 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}}