Оновлений відповідь - найкращі частини всіх інших відповідей
Я описую рішення для різних випадків використання і також би вирішував нескінченну проблему рекурсії
Випадок 1: Ви керуєте класами , тобто ви можете написати свої власні Cat
, Dog
класи, а також IAnimal
інтерфейс. Ви можете просто дотримуватися рішення, яке надає @ marcus-junius-brutus (найкраща відповідь)
Не буде нескінченної рекурсії, якщо є загальний базовий інтерфейс як IAnimal
Але що робити, якщо я не хочу реалізувати IAnimal
або будь-який подібний інтерфейс?
Тоді @ marcus-junius-brutus (найкраща відповідь) створить нескінченну помилку рекурсії. У цьому випадку ми можемо зробити щось подібне нижче.
Ми повинні створити конструктор копій всередині базового класу та підкласу обгортки таким чином:
.
// 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;
}
}
Отже, чому конструктор копій?
Щойно, коли ви визначите конструктор копій, незалежно від того, наскільки змінився базовий клас, ваша обгортка продовжить ту саму роль. По-друге, якщо ми не визначимо конструктор копій і просто підкласиємо базовий клас, тоді нам доведеться "говорити" з точки зору розширеного класу, тобто 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
Випадок 2: Ви не контролюєте заняття . Ви приєднуєтесь до компанії або використовуєте бібліотеку, де класи вже визначені, і ваш менеджер не хоче, щоб ви їх змінювали жодним чином - Ви можете підкласифікувати свої класи та змусити їх реалізувати загальний інтерфейс маркера (який не має методів ) такі як AnimalInterface
.
Наприклад:
.
// 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}}