Обновленный ответ - Лучшие части всех остальных ответов
Я описываю решение для различных случаев применения и буду решение бесконечной рекурсии проблемы , а также
Случай 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» in 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}}