Обновленный ответ - лучшие части всех других ответов
Я описываю решения для различных вариантов использования, а также рассматриваю проблему бесконечной рекурсии.
Случай 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();
Здесь вам нужно будет ввести поле под названием «тип» в Animal
и значение того же внутри Dog
как «собака», Cat
как «кошка».
Полный пример: https://static.javadoc.io/org.danilopianini/gson-extras/0.2.1/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.html
Случай 2: Вы не контролируете классы. Вы присоединяетесь к компании или используете библиотеку, в которой классы уже определены, и ваш менеджер не хочет, чтобы вы их каким-либо образом изменяли. Вы можете создавать подклассы своих классов и реализовывать общий интерфейс маркеров (у которого нет никаких методов). ), например AnimalInterface
.
Ex:
.
// 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}}
person
Manish Kumar Sharma
schedule
15.08.2019