Câu trả lời được cập nhật - Phần hay nhất của tất cả các câu trả lời khác
Tôi đang mô tả các giải pháp cho các trường hợp sử dụng khác nhau và cũng sẽ giải quyết vấn đề đệ quy vô hạn
Trường hợp 1: Bạn đang ở trong kiểm soát của các lớp học , ví dụ, bạn có thể viết riêng của bạn Cat
, Dog
lớp học cũng như IAnimal
giao diện. Bạn chỉ cần làm theo giải pháp được cung cấp bởi @ marcus-junius-britus (câu trả lời được xếp hạng cao nhất)
Sẽ không có bất kỳ đệ quy vô hạn nào nếu có một giao diện cơ sở chung như IAnimal
Nhưng, điều gì sẽ xảy ra nếu tôi không muốn triển khai IAnimal
hoặc bất kỳ giao diện nào như vậy?
Sau đó, @ marcus-junius-britus (câu trả lời được xếp hạng cao nhất) sẽ tạo ra lỗi đệ quy vô hạn. Trong trường hợp này, chúng ta có thể làm như sau.
Chúng ta sẽ phải tạo một phương thức khởi tạo sao chép bên trong lớp cơ sở và một lớp con của trình bao bọc như sau:
.
// 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);
}
}
Và bộ nối tiếp cho loại 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;
}
}
Vì vậy, tại sao một hàm tạo bản sao?
Chà, một khi bạn xác định hàm tạo bản sao, bất kể lớp cơ sở thay đổi bao nhiêu, trình bao bọc của bạn sẽ tiếp tục với vai trò tương tự. Thứ hai, nếu chúng ta không định nghĩa một phương thức khởi tạo sao chép và chỉ đơn giản là lớp con của lớp cơ sở thì chúng ta sẽ phải "nói chuyện" về lớp mở rộng, tức là CatWrapper
,. Rất có thể các thành phần của bạn nói về lớp cơ sở chứ không phải kiểu trình bao bọc.
Có một giải pháp thay thế dễ dàng?
Chắc chắn, nó hiện đã được giới thiệu bởi Google - đây là cách RuntimeTypeAdapterFactory
triển khai:
RuntimeTypeAdapterFactory<Animal> runtimeTypeAdapterFactory = RuntimeTypeAdapterFactory
.of(Animal.class, "type")
.registerSubtype(Dog.class, "dog")
.registerSubtype(Cat.class, "cat");
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(runtimeTypeAdapterFactory)
.create();
Ở đây, bạn cần giới thiệu một trường có tên là "type" trong Animal
và giá trị của cùng bên trong Dog
là "dog", Cat
là "cat"
Toàn bộ ví dụ: 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);
}
}
Vì vậy, chúng tôi sẽ sử dụng CatWrapper
thay vì Cat
, DogWrapper
thay vì Dog
và
AlternativeAnimalAdapter
thay vì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);
}
}
Chúng tôi thực hiện một bài kiểm tra:
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);
}
}
}
Đầu ra:
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}}