คำตอบที่อัปเดต - ส่วนที่ดีที่สุดของคำตอบอื่น ๆ ทั้งหมด
ฉันกำลังอธิบายวิธีแก้ปัญหาสำหรับกรณีการใช้งานที่หลากหลายและจะจัดการกับปัญหาการเรียกซ้ำที่ไม่สิ้นสุดเช่นกัน
กรณีที่ 1: คุณอยู่ในการควบคุมของการเรียนคือคุณจะได้รับการเขียนของคุณเองCat
, Dog
การเรียนเช่นเดียวกับIAnimal
อินเตอร์เฟซ คุณสามารถทำตามวิธีแก้ปัญหาโดย @ marcus-junius-brutus (คำตอบยอดนิยม)
จะไม่มีการเรียกซ้ำไม่สิ้นสุดหากมีอินเทอร์เฟซพื้นฐานทั่วไปเป็น IAnimal
แต่ถ้าฉันไม่ต้องการใช้IAnimal
อินเทอร์เฟซหรือส่วนต่อประสานดังกล่าวจะเป็นอย่างไร
จากนั้น @ marcus-junius-brutus (คำตอบที่ได้รับคะแนนสูงสุด) จะสร้างข้อผิดพลาดซ้ำไม่สิ้นสุด ในกรณีนี้เราสามารถทำสิ่งต่างๆดังต่อไปนี้
เราจะต้องสร้างตัวสร้างการคัดลอกภายในคลาสพื้นฐานและคลาสย่อยของ wrapper ดังนี้:
.
// 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);
}
}
และซีเรียลไลเซอร์สำหรับประเภท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;
}
}
เหตุใดจึงต้องมีตัวสร้างสำเนา
เมื่อคุณกำหนดตัวสร้างการคัดลอกแล้วไม่ว่าคลาสพื้นฐานจะเปลี่ยนไปมากแค่ไหน Wrapper ของคุณจะยังคงมีบทบาทเดิมต่อไป ประการที่สองถ้าเราไม่กำหนดตัวสร้างการคัดลอกและเพียงแค่ซับคลาสคลาสพื้นฐานเราก็จะต้อง "พูดคุย" ในแง่ของคลาสขยายนั่นคือCatWrapper
. ค่อนข้างเป็นไปได้ที่ส่วนประกอบของคุณจะพูดในแง่ของคลาสพื้นฐานไม่ใช่ประเภทของกระดาษห่อหุ้ม
มีทางเลือกอื่นที่ง่ายหรือไม่?
แน่นอนว่า Google ได้เปิดตัวแล้ว - นี่คือการRuntimeTypeAdapterFactory
ใช้งาน:
RuntimeTypeAdapterFactory<Animal> runtimeTypeAdapterFactory = RuntimeTypeAdapterFactory
.of(Animal.class, "type")
.registerSubtype(Dog.class, "dog")
.registerSubtype(Cat.class, "cat");
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(runtimeTypeAdapterFactory)
.create();
ที่นี่คุณจะต้องแนะนำฟิลด์ที่เรียกว่า "type" ในAnimal
และค่าของด้านในเดียวกันDog
คือ "dog" Cat
เป็น "cat"
ตัวอย่างที่สมบูรณ์: 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);
}
}
ดังนั้นเราจะใช้CatWrapper
แทนCat
, DogWrapper
แทนDog
และ
AlternativeAnimalAdapter
แทนIAnimalAdapter
// 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);
}
}
เราทำการทดสอบ:
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);
}
}
}
เอาท์พุต:
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}}