簡體   English   中英

使用泛型類型和通用字段名稱進行GSON反序列化

[英]GSON deserialization with generic types and generic field names

假設我們有這樣的結構:

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;

但是,現在我們說我們有相同的結構,但我們收到truck而不是Cat

{
  "body": {
    "trucks": [
      {
        "truck": {
          "id": 1,
          "engine": "big",
          "wheels": 12
        }
      },
      {
        "truck": {
          "id": 2,
          "engine": "super big",
          "wheels": 128
        }
      }
    ]
  }
}

我在Android上使用GSON(默認的Retrofit json解析器),在給出解決方案的同時考慮這一點。

我們可以在這里使用泛型,所以響應看起來像:

private final Body<ListResponse<ItemWrapper<T>>但我們不能,因為字段名稱是特定於類的。

題:

如果不創建如此多的樣板類,我可以自動序列化它嗎? 我真的不需要像BodyCatBodyTruckBodyAnyOtherClassInThisStructure這樣的單獨的類,我正在尋找避免使用它們的方法。

@編輯:

由於繼承混淆,我改變了類(貓,狗 - >貓,卡車),這里使用的類作為示例不要互相擴展

一個想法是定義一個自定義通用解串器。 它的泛型類型將表示包含在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的結構總是相同的,因為您有一個包含名為“body”的對象的對象。 此外,它假定此正文的第一個鍵值對中的值是一個列表。

現在,對於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_big_12, 2_super big_128]作為輸出。

我會用這種方法:

public class Body {
    private List<Animal> animals;
   }
}

public class Animal {}

public class Dog extends Animal {}

public class Cat extends Animal {}

在這種情況下,除了必須使用Gson TypeAdapter for Animal類的事實之外,你TypeAdapter所有的樣板都進行序列化,例如:

Gson gson = new GsonBuilder()
                    .registerTypeAdapter(Animal.class, new AnimalSerializer())
                    .create();

TypeAdapter看起來像smth:

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;
    }
}
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類,其中包含只是實際動物列表的類列表。 它應該讓你只需要你的身體類,然后用它的統計數據為每個動物分類。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM