[英]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>>
但我們不能,因為字段名稱是特定於類的。
題:
如果不創建如此多的樣板類,我可以自動序列化它嗎? 我真的不需要像BodyCat
, BodyTruck
, BodyAnyOtherClassInThisStructure
這樣的單獨的類,我正在尋找避免使用它們的方法。
@編輯:
由於繼承混淆,我改變了類(貓,狗 - >貓,卡車),這里使用的類作為示例不要互相擴展
一個想法是定義一個自定義通用解串器。 它的泛型類型將表示包含在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.