小编典典

具有通用类型和通用字段名称的GSON反序列化

java

假设我们有这样的结构:

JSON:

{
  "body": {
    "cats": [
      {
        "cat": {
          "id": 1,
          "title": "cat1"
        }
      },
      {
        "cat": {
          "id": 2,
          "title": "cat2"
        }
      }
    ]
  }
}

和对应的POJO:

响应类

private final Body body;

身体类

private final Collection<CatWrapper> cats

CatWrapper.class

private final Cat cat

类别

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
        }
      }
    ]
  }
}

我在上使用GSON(默认Retrofitjson解析器)Android,在提供解决方案时请考虑一下。

我们可以在此处使用泛型,因此响应如下所示:

private final Body<ListResponse<ItemWrapper<T>> 但是我们不能,因为字段名称是特定于类的。

题:

我如何做才能自动序列化它而不创建太多样板类?我并不真的需要单独的类一样BodyCatBodyTruckBodyAnyOtherClassInThisStructure和我正在寻找一种方法来避免它们。

@编辑:

由于继承混乱,我更改了类(cat,dog-> cat,truck),此处用作示例的类不要互相扩展


阅读 295

收藏
2020-11-16

共1个答案

小编典典

一种想法是定义一个自定义的通用解串器。它的通用类型将表示包装在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;

    ...
}

从您拥有包含名为“ body”的对象的意义上说,反序列化器假定json的结构始终相同。还假定此主体的第一个键值对中的值是一个列表。

现在,对于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]有了猫。

2020-11-16