使用泛型类型和泛型字段名称的GSON反序列化 [英] GSON deserialization with generic types and generic field names

查看:222
本文介绍了使用泛型类型和泛型字段名称的GSON反序列化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我们有这样的结构:
$ 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>> {

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 class。



使用卡车,您将获得 [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 receive truck

{
    "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) on Android, 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆