Я думаю, что пользовательский сериализатор/десериализатор - единственный способ продолжения, и я попытался предложить вам самый компактный способ реализовать его, который я нашел. Я извиняюсь за то, что не использовал ваши классы, но идея такая же (мне просто нужен как минимум 1 базовый класс и 2 расширенных класса).
BaseClass.java
public class BaseClass{
@Override
public String toString() {
return "BaseClass [list=" + list + ", isA=" + isA + ", x=" + x + "]";
}
public ArrayList<BaseClass> list = new ArrayList<BaseClass>();
protected String isA="BaseClass";
public int x;
}
ExtendedClass1.java
public class ExtendedClass1 extends BaseClass{
@Override
public String toString() {
return "ExtendedClass1 [total=" + total + ", number=" + number
+ ", list=" + list + ", isA=" + isA + ", x=" + x + "]";
}
public ExtendedClass1(){
isA = "ExtendedClass1";
}
public Long total;
public Long number;
}
ExtendedClass2.java
public class ExtendedClass2 extends BaseClass{
@Override
public String toString() {
return "ExtendedClass2 [total=" + total + ", list=" + list + ", isA="
+ isA + ", x=" + x + "]";
}
public ExtendedClass2(){
isA = "ExtendedClass2";
}
public Long total;
}
CustomDeserializer.java
public class CustomDeserializer implements JsonDeserializer<List<BaseClass>> {
private static Map<String, Class> map = new TreeMap<String, Class>();
static {
map.put("BaseClass", BaseClass.class);
map.put("ExtendedClass1", ExtendedClass1.class);
map.put("ExtendedClass2", ExtendedClass2.class);
}
public List<BaseClass> deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
List list = new ArrayList<BaseClass>();
JsonArray ja = json.getAsJsonArray();
for (JsonElement je : ja) {
String type = je.getAsJsonObject().get("isA").getAsString();
Class c = map.get(type);
if (c == null)
throw new RuntimeException("Unknow class: " + type);
list.add(context.deserialize(je, c));
}
return list;
}
}
CustomSerializer.java
public class CustomSerializer implements JsonSerializer<ArrayList<BaseClass>> {
private static Map<String, Class> map = new TreeMap<String, Class>();
static {
map.put("BaseClass", BaseClass.class);
map.put("ExtendedClass1", ExtendedClass1.class);
map.put("ExtendedClass2", ExtendedClass2.class);
}
@Override
public JsonElement serialize(ArrayList<BaseClass> src, Type typeOfSrc,
JsonSerializationContext context) {
if (src == null)
return null;
else {
JsonArray ja = new JsonArray();
for (BaseClass bc : src) {
Class c = map.get(bc.isA);
if (c == null)
throw new RuntimeException("Unknow class: " + bc.isA);
ja.add(context.serialize(bc, c));
}
return ja;
}
}
}
и теперь это код, я выполнил, чтобы проверить все это:
public static void main(String[] args) {
BaseClass c1 = new BaseClass();
ExtendedClass1 e1 = new ExtendedClass1();
e1.total = 100L;
e1.number = 5L;
ExtendedClass2 e2 = new ExtendedClass2();
e2.total = 200L;
e2.x = 5;
BaseClass c2 = new BaseClass();
c1.list.add(e1);
c1.list.add(e2);
c1.list.add(c2);
List<BaseClass> al = new ArrayList<BaseClass>();
// this is the instance of BaseClass before serialization
System.out.println(c1);
GsonBuilder gb = new GsonBuilder();
gb.registerTypeAdapter(al.getClass(), new CustomDeserializer());
gb.registerTypeAdapter(al.getClass(), new CustomSerializer());
Gson gson = gb.create();
String json = gson.toJson(c1);
// this is the corresponding json
System.out.println(json);
BaseClass newC1 = gson.fromJson(json, BaseClass.class);
System.out.println(newC1);
}
Это мое исполнение :
BaseClass [list=[ExtendedClass1 [total=100, number=5, list=[], isA=ExtendedClass1, x=0], ExtendedClass2 [total=200, list=[], isA=ExtendedClass2, x=5], BaseClass [list=[], isA=BaseClass, x=0]], isA=BaseClass, x=0]
{"list":[{"total":100,"number":5,"list":[],"isA":"ExtendedClass1","x":0},{"total":200,"list":[],"isA":"ExtendedClass2","x":5},{"list":[],"isA":"BaseClass","x":0}],"isA":"BaseClass","x":0}
BaseClass [list=[ExtendedClass1 [total=100, number=5, list=[], isA=ExtendedClass1, x=0], ExtendedClass2 [total=200, list=[], isA=ExtendedClass2, x=5], BaseClass [list=[], isA=BaseClass, x=0]], isA=BaseClass, x=0]
Некоторые объяснения: трюк выполняется другим Gson внутри сериализатора/десериализатора. Я использую только поле isA
, чтобы найти подходящий класс. Чтобы идти быстрее, я использую карту, чтобы связать строку isA
с соответствующим классом. Затем я делаю правильную сериализацию/десериализацию с использованием второго объекта Gson. Я объявил это как статический, поэтому вы не будете замедлять сериализацию/десериализацию с множественным распределением Gson.
Pro Вы на самом деле не пишете код больше кода, чем этот, вы позволяете Gson выполнять всю работу. Вы должны помнить о том, чтобы добавить новый подкласс в карты (это напоминает об этом исключение).
Против У вас есть две карты. Я думаю, что моя реализация может немного уточнить, чтобы избежать дублирования карт, но я оставил их вам (или будущему редактору, если таковые имеются).
Возможно, вы хотите унифицировать сериализацию и десериализацию в уникальный объект, вы должны проверить класс или эксперимент TypeAdapter
с объектом, который реализует оба интерфейса.
Я не проверил десериализацию еще, но для сериализации есть проблема. Если вы проверите мой класс ObixBaseObj, у меня есть arraylist этого базового типа, который добавляется как часть его дочернего элемента. В вашем примере вы непосредственно сериализуете arraylist базового слоя, но в моем случае я сериализую один базовый объект, содержащий список массивов полиморфного объекта. В этом случае сериализация не работает должным образом. – l46kok
Чтобы быть более конкретным, если я добавлю в свой BaseClass частный элемент массива ArrayList , добавьте объекты, которые наследуются от BaseClass к этому списку (ExtendedClass1,2), а затем попытаются сериализовать единственный экземпляр BaseClass, он неправильно упорядочивает содержащий arraylist. – l46kok
На самом деле, аналогичная проблема для десериализации тоже. Все становится десериализованным как базовый класс. – l46kok