使用泛型类型和泛型字段名称的GSON反序列化 [英] GSON deserialization with generic types and generic field names
问题描述
假设我们有这样的结构:
$ b
JSON:
{
body:{
cats:[{
cat:{
id:1,
title:cat1
}
},{
cat:{
id:2,
title:cat2
$ p > 和相应的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
我们收到卡车
{
body:{
trucks:[{
truck:{
id:1,
engine:big,
车轮:12
}
},{
卡车:{
id:2,
引擎:超级大,
wheels:128
}
}]
}
}
我在
Android
上使用GSON(默认Retrofit
json解析器),考虑同时提供解决方案。
我们可以在这里使用泛型,所以响应看起来像:
private final Body< ListResponse< ItemWrapper< T>>
但我们不能,因为字段名称是特定于类的。
问题:
如何自动序列化而不创建许多样板类?我真的不需要单独的类,如
BodyCat
,BodyTruck
,BodyAnyOtherClassInThisStructure
,我正在寻找一种避免拥有它们的方法。
@EDIT:
我已经改变班级(猫,狗 - >猫,卡车)继承混淆,这里使用的类作为例子不要互相延伸
解决方案一个想法是定义一个自定义的通用解串器。它的泛型将表示包装在
Body
实例中的列表元素的具体类。
假设以下类:
class Body< T> {
私人列表< T>列表;
public Body(List< T> list){
this.list = list;
}
}
class Cat {
private int id;
私有字符串标题;
...
}
类卡车{
private int id;
私有字符串引擎;
私人诠释车轮;
}
解串器假定json的结构总是相同的,因为你有一个包含名为body的对象的对象。此外,它假设本体的第一个键值对中的值是一个列表。
现在对于json数组中的每个元素,我们需要再次获取与每个键相关联的内部对象。我们反序列化这个值并将它放在列表中。
class CustomJsonDeserializer< T>实现JsonDeserializer< Body< T>> {
class。
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();
列表< T> list = new ArrayList<>(); (JsonElement元素:arr){
JsonElement innerElement = element.getAsJsonObject()。entrySet()。iterator()。next()。getValue();
list.add(context.deserialize(innerElement,clazz));
}
返回新的Body<>(list);
$ b $ p
$ b最后一步,你只需要创建相应的类型,在解析器中实例化并注册适配器。例如,对于卡车:
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);
如果您想要摆脱<$ c,您甚至可以直接从适配器返回列表$ c $> Body
使用卡车,您将获得
[1_big_12,2_super big_128]
作为输出,[1_cat1,2_cat2] 与猫。 Let's say we have structure like this:
JSON:
{ "body": { "cats": [{ "cat": { "id": 1, "title": "cat1" } }, { "cat": { "id": 2, "title": "cat2" } }] } }
And corresponding 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;
But let say now we have the same structure, but instead of
Cat
we receivetruck
{ "body": { "trucks": [{ "truck": { "id": 1, "engine": "big", "wheels" : 12 } }, { "truck": { "id": 2, "engine": "super big", "wheels" : 128 } }] } }
I'm using GSON (default
Retrofit
json parser) onAndroid
, consider this while giving solutions.We could use generics here so response would look like:
private final Body<ListResponse<ItemWrapper<T>>
but we can't since the field names are specific to a class.QUESTION:
What Can I do to serialize it automaticaly without creating so many boilerplate classes? I don't really need separate classes like
BodyCat
,BodyTruck
,BodyAnyOtherClassInThisStructure
and I'm looking for a way to avoid having them.@EDIT:
I've change classes (cat, dog -> cat,truck) due to inheritance confusion, classes used here as example DO NOT extends one another
解决方案One idea would be to define a custom generic deserializer. Its generic type will represent the concrete class of the list's elements wrapped in a
Body
instance.Assuming the following classes:
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; ... }
The deserializer assumes that the structure of the json is always the same, in the sense that you have an object that contains an object named "body". Also it assumes that the value in the first key-value pair of this body is a list.
Now for each element in the json array, we need again to fetch the inner object associated with each key. We deserialize this value and put it in the list.
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); } }
For the final step, you just need to create the corresponding type, instantiate and register the adapter in the parser. For instance for trucks:
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);
You can even return the list directly from the adapter if you want to get rid of the
Body
class.With the trucks you'll get
[1_big_12, 2_super big_128]
as output and[1_cat1, 2_cat2]
with the cats.这篇关于使用泛型类型和泛型字段名称的GSON反序列化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!