Güncellenmiş Cevap - Diğer tüm cevapların en iyi kısımları
Çeşitli kullanım durumları için çözümler anlatıyorum ve aynı zamanda sonsuz yineleme sorununu da ele alıyorum
Durum 1: Sen sınıfların kontrolünde olan yani, kendi yazmak olsun Cat
, Dog
hem sınıflar hem deIAnimal
arayüz. @ Marcus-junius-brutus tarafından sağlanan çözümü kolayca takip edebilirsiniz (en yüksek puan alan cevap)
Ortak bir temel arayüz varsa sonsuz özyineleme olmayacaktır. IAnimal
Peki IAnimal
ya bu ya da benzeri bir arayüzü uygulamak istemiyorsam ?
Ardından, @ marcus-junius-brutus (en yüksek puan alan cevap) sonsuz bir özyineleme hatası üretecektir. Bu durumda aşağıdaki gibi bir şey yapabiliriz.
Temel sınıf içinde bir kopya oluşturucu ve aşağıdaki gibi bir sarmalayıcı alt sınıfı oluşturmamız gerekir :
.
// 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);
}
}
Ve tür için serileştirici 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;
}
}
Peki neden bir kopya oluşturucu?
Kopyalama yapıcısını tanımladığınızda, temel sınıf ne kadar değişirse değişsin, sarmalayıcınız aynı rolle devam edecektir. İkinci olarak, eğer bir kopya oluşturucu tanımlamazsak ve basitçe temel sınıfı alt sınıfa ayırmazsak, genişletilmiş sınıf açısından "konuşmamız" gerekir, yani,CatWrapper
. Bileşenlerinizin sarmalayıcı türü değil, temel sınıf açısından konuşması oldukça olasıdır.
Kolay bir alternatif var mı?
Elbette, artık Google tarafından tanıtıldı - RuntimeTypeAdapterFactory
uygulama şu şekildedir:
RuntimeTypeAdapterFactory<Animal> runtimeTypeAdapterFactory = RuntimeTypeAdapterFactory
.of(Animal.class, "type")
.registerSubtype(Dog.class, "dog")
.registerSubtype(Cat.class, "cat");
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(runtimeTypeAdapterFactory)
.create();
Burada, "tür" adı verilen bir alan Animal
ve aynı içerideki değeri Dog
"köpek", Cat
"kedi" olarak girmeniz gerekir.
Tam örnek: https://static.javadoc.io/org.danilopianini/gson-extras/0.2.1/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.html
.
// 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);
}
}
Yani, biz kullanarak olurdu CatWrapper
yerine Cat
, DogWrapper
yerine Dog
ve
AlternativeAnimalAdapter
yerineIAnimalAdapter
// 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);
}
}
Bir test yapıyoruz:
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);
}
}
}
Çıktı:
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}}