我们说我们有这样的结构:
JSON:
{
"body": {
"cats": [{
"cat": {
"id": 1,
"title": "cat1"
}
}, {
"cat": {
"id": 2,
"title": "cat2"
}
}]
}
}
和相应的POJO:
Response.class
private final Body body;
Body.class
private final Collection<CatWrapper> cats
CatWrapper.class
private final Cat cat
Cat.class
private final int id;
private final String title;
但是现在我们说我们有相同的结构,但我们收到Cat
而不是truck
{
"body": {
"trucks": [{
"truck": {
"id": 1,
"engine": "big",
"wheels" : 12
}
}, {
"truck": {
"id": 2,
"engine": "super big",
"wheels" : 128
}
}]
}
}
我在Retrofit
上使用GSON(默认Android
json解析器),在给出解决方案的同时考虑这一点。
我们可以在这里使用泛型,所以响应看起来像:
private final Body<ListResponse<ItemWrapper<T>>
但我们不能,因为字段名称是特定于类的。
问题:
如果不创建如此多的样板类,我可以自动序列化它吗?我并不需要单独的课程,例如BodyCat
,BodyTruck
,BodyAnyOtherClassInThisStructure
,而我正在寻找避免使用它们的方法。
@EDIT:
由于继承混淆,我改变了类(猫,狗 - >猫,卡车),这里使用的类例如不要相互延伸
答案 0 :(得分:7)
一个想法是定义一个自定义通用反序列化器。它的泛型类型将表示包含在Body
实例中的列表元素的具体类。
假设以下课程:
class Body<T> {
private List<T> list;
public Body(List<T> list) {
this.list = list;
}
}
class Cat {
private int id;
private String title;
...
}
class Truck {
private int id;
private String engine;
private int wheels;
...
}
反序列化器假定json的结构总是相同的,因为你有一个包含名为&#34; body&#34;的对象的对象。此外,它假定此正文的第一个键值对中的值是一个列表。
现在,对于json数组中的每个元素,我们需要再次获取与每个键关联的内部对象。我们反序列化该值并将其放在列表中。
class CustomJsonDeserializer<T> implements JsonDeserializer<Body<T>> {
private final Class<T> clazz;
public CustomJsonDeserializer(Class<T> clazz) {
this.clazz = clazz;
}
@Override
public Body<T> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject body = json.getAsJsonObject().getAsJsonObject("body");
JsonArray arr = body.entrySet().iterator().next().getValue().getAsJsonArray();
List<T> list = new ArrayList<>();
for(JsonElement element : arr) {
JsonElement innerElement = element.getAsJsonObject().entrySet().iterator().next().getValue();
list.add(context.deserialize(innerElement, clazz));
}
return new Body<>(list);
}
}
对于最后一步,您只需要创建相应的类型,在解析器中实例化并注册适配器。例如卡车:
Type truckType = new TypeToken<Body<Truck>>(){}.getType();
Gson gson = new GsonBuilder()
.registerTypeAdapter(truckType, new CustomJsonDeserializer<>(Truck.class))
.create();
Body<Truck> body = gson.fromJson(new FileReader("file.json"), truckType);
如果你想摆脱Body
类,你甚至可以直接从适配器返回列表。
使用卡车,您将获得[1_big_12, 2_super big_128]
作为输出,[1_cat1, 2_cat2]
与猫一起。
答案 1 :(得分:2)
我会用这种方法:
public class Body {
private List<Animal> animals;
}
}
public class Animal {}
public class Dog extends Animal {}
public class Cat extends Animal {}
在这种情况下,除了必须对TypeAdapter
类使用Gson Animal
这样的事实外,您将对所有样板进行序列化,例如:
Gson gson = new GsonBuilder()
.registerTypeAdapter(Animal.class, new AnimalSerializer())
.create();
TypeAdapter应该看起来像:
public class AnimalSerializer implements JsonSerializer<Animal>, JsonDeserializer<Animal> {
private static final String CLASS_META_KEY="clz";
@Override
public JsonElement serialize(Animal src, Type typeOfSrc,
JsonSerializationContext context) {
JsonElement element=null;
if (src == null) {
return element;
}
if(src instanceof Cat)
element = context.serialize(src, Cat.class);
else if(src instanceof Dog)
element = context.serialize(src, Dog.class);
else
throw new IllegalArgumentException("Unspecifiad class serializer for "+src.getClass().getName());
element.getAsJsonObject().addProperty(CLASS_META_KEY, src.getClass().getCanonicalName());
return element;
}
@Override
public Field deserialize(JsonElement jsonElement, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
Class<?> clz;
Animal animal;
JsonObject object = jsonElement.getAsJsonObject();
if (object.has(CLASS_META_KEY)) {
String className = object.get(CLASS_META_KEY).getAsString();
try {
clz = Class.forName(className);
} catch (Exception e) {
Log.e(TAG, "Can't deserialize class="+className,e);
clz = Animal.class;
}
animal = context.deserialize(jsonElement, clz);
} else {
animal = context.deserialize(jsonElement, typeOfT);
}
return animal;
}
}
答案 2 :(得分:-1)
public class body {
private List<cats> cats;
private List<dogs> dogs;
public class cats {
private list<cat> cat;
}
public class dogs {
private list<dog> dog;
}
}
这并没有真正减少样板代码,但它应该会阻止你有一个单独的body类,其中的类列表只是你真实动物的列表。它应该让你只需要你的身体类,然后用每个动物的类来获得它的统计数据。