我一直在使用Retrofit 2和一些POJO对象一段时间了。这是一个可爱的图书馆,效果很好,但它需要一些我想要摆脱的可怕和混乱的模型。
我会告诉你......我有以下JSON要仔细阅读:
{
"things": [
{
"thing": {
"id": 823,
"created_at": "2016-02-09T22:55:07.153Z",
"published_at": "2016-02-10T19:23:42.666Z",
"downloads": 16073,
"size": 10716291
}
},
],
"count": 4,
"links": {}
}
使用POJO Schema生成器会创建不必要的类,这使得难以维护代码。
这将创建:
Things.java
@SerializedName("things")
@Expose
public List<Things_> things = new ArrayList<>();
Things_.java
@SerializedName("thing")
@Expose
private Thing__ thing;
Things__.java
// Insert normal variables and getter/setters here
我已经减少了一点,因为这只是为了这个想法。在我的使用中,我当然重命名了这些类,使它们更容易管理。但我认为有一种方法可以简单地跳过Thing和Thing_并允许我只返回一个实际模型数据列表(Thing__),这两个类可以删除,而“Thing__”可以简单地为“Thing”。
我是对的。 Gson允许自定义反序列化,让我实现这一目标。我把一个快速的反序列化器组合在一起并使用了一个合适的TypeToken
Gson gson = new GsonBuilder()
.registerTypeAdapter(new TypeToken<ArrayList<Thing>>(){}.getType(), new AddonDeserializer())
.create();
List<Thing> model = gson.fromJson(jsonString, new TypeToken<ArrayList<Thing>>(){}.getType());
果然,通过上面那个确切的Json给我一个可用的东西列表。
输入Retrofit 2!将registerTypeAdapter()添加到我的Retrofit 2实例(通过我的gson实例)后,我现在收到一条错误消息:
Expected BEGIN_ARRAY but was BEGIN_OBJECT
这是因为我的电话可能是:
@Get("end/of/url/here/{slug}.json")
Call<List<Thing>> getThings(@Path("slug") String slug);
我的Json以一个包含“thing”对象数组的对象(“things”)开头。我的解串器对此没有任何问题:
public class ThingDeserializer implements JsonDeserializer<List<Thing>> {
@Override
public List<Thing> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonArray array = json.getAsJsonObject().getAsJsonArray("things");
ArrayList<Thing> list = new ArrayList<>();
for (JsonElement anArray : array) {
list.add((Thing) context.deserialize(anArray.getAsJsonObject().get("thing").getAsJsonObject(), Thing.class));
}
return list;
}
}
无论如何,感谢你坚持这个很长的问题!
我需要做些什么,或者我如何操纵Retrofit与我写的Gson Deserializer一样?我有什么工作,但为了学习新的东西,编写更好,更易维护的代码,我想弄清楚这一点。我可以使用ResponseBody回调并通过我的反序列化器抛出Json,但必须有一个更好的方法。
答案 0 :(得分:5)
感谢@JonathanAste我明白了。
我需要一个TypeAdapterFactory实现,而不是Deserializer。
public class ThingTypeAdapterFactory implements TypeAdapterFactory {
public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
return new TypeAdapter<T>() {
public void write(JsonWriter out, T value) throws IOException {
delegate.write(out, value);
}
public T read(JsonReader in) throws IOException {
JsonElement jsonElement = elementAdapter.read(in);
if (jsonElement.isJsonObject()) {
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (jsonObject.has("things") && jsonObject.get("things").isJsonArray())
{
jsonElement = jsonObject.get("things");
}
}
if (jsonElement.isJsonObject()) {
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (jsonObject.has("thing") && jsonObject.get("thing").isJsonObject())
{
jsonElement = jsonObject.get("thing");
}
}
return delegate.fromJsonTree(jsonElement);
}
}.nullSafe();
}
}
这样你就可以使用
了@GET("end/of/url/here/{slug}.json")
Call<List<Thing>> getThings(@Path("slug") String slug);
没有问题。