Jawaban yang Diperbarui - Bagian terbaik dari semua jawaban lainnya
Saya menjelaskan solusi untuk berbagai kasus penggunaan dan akan menangani masalah rekursi tak terbatas juga
Kasus 1: Anda mengendalikan kelas , yaitu, Anda bisa menulis kelas Anda sendiri Cat
, Dog
serta kelasIAnimal
antarmuka. Anda cukup mengikuti solusi yang disediakan oleh @ marcus-junius-brutus (jawaban teratas)
Tidak akan ada rekursi tak terbatas jika ada antarmuka dasar yang sama seperti IAnimal
Tetapi, bagaimana jika saya tidak ingin mengimplementasikan IAnimal
atau antarmuka semacam itu?
Kemudian, @ marcus-junius-brutus (jawaban teratas) akan menghasilkan kesalahan rekursi tak terbatas. Dalam hal ini, kita dapat melakukan sesuatu seperti di bawah ini.
Kita harus membuat konstruktor salinan di dalam kelas dasar dan subkelas pembungkus sebagai berikut:
.
// 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);
}
}
Dan serializer untuk tipe 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;
}
}
Jadi, mengapa pembuat salinan?
Nah, setelah Anda menentukan konstruktor salinan, tidak peduli seberapa banyak kelas dasar berubah, pembungkus Anda akan melanjutkan dengan peran yang sama. Kedua, jika kita tidak mendefinisikan konstruktor salinan dan hanya membuat subkelas kelas dasar maka kita harus "berbicara" dalam istilah kelas yang diperluas, yaitu,CatWrapper
. Sangat mungkin bahwa komponen Anda berbicara dalam istilah kelas dasar dan bukan jenis pembungkusnya.
Apakah ada alternatif yang mudah?
Tentu, sekarang telah diperkenalkan oleh Google - inilah RuntimeTypeAdapterFactory
implementasinya:
RuntimeTypeAdapterFactory<Animal> runtimeTypeAdapterFactory = RuntimeTypeAdapterFactory
.of(Animal.class, "type")
.registerSubtype(Dog.class, "dog")
.registerSubtype(Cat.class, "cat");
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(runtimeTypeAdapterFactory)
.create();
Di sini, Anda perlu memasukkan Animal
kolom yang disebut "type" in dan nilai di dalamnya yang sama Dog
menjadi "dog", Cat
menjadi "cat"
Contoh lengkap: 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);
}
}
Jadi, kami akan menggunakan CatWrapper
alih- alih Cat
, DogWrapper
alih- alih Dog
dan
AlternativeAnimalAdapter
alih-alihIAnimalAdapter
// 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);
}
}
Kami melakukan tes:
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);
}
}
}
Keluaran:
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}}