android - Retrofit 2、GSON 和自定义解串器

标签 android gson deserialization retrofit

我使用 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 以一个对象(“事物”)开始,它包含一个“事物”对象数组。我的反序列化器对此没有问题:

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 回调并通过我的 Deserializer 抛出 Json,但必须有更好的方法。

最佳答案

感谢@JonathanAste,我弄明白了。

我需要一个 TypeAdapterFactory 实现而不是反序列化器。

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);

没有问题。

关于android - Retrofit 2、GSON 和自定义解串器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43455825/

相关文章:

android - 带有 CursorLoader 的自动完成 TextView

java - 使用 GSON 解析 key - 对象 JSON

c# - 无法在 .NET 中反序列化 JSON 字符串

WCF序列化代码

android - 如何禁用 Android 上的切换按钮?

java - 如何从 Intent 迭代自定义对象的 ArrayList 并将它们添加到 LinearLayout 中?

android - Rxjava,如何推迟发射直到有多个订阅者订阅?

java - RuntimeTypeAdapterFactory 说 “type” 未定义

java - JsonParseException 反序列化对象失败

c++ - 异常:内存位置的 boost::archive::archive_exception