草庐IT

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

coder 2023-12-27 原文

我使用 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 - Retrofit 2、GSON 和自定义解串器的更多相关文章

  1. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  2. ruby-on-rails - 如何生成传递一些自定义参数的 `link_to` URL? - 2

    我正在使用RubyonRails3.0.9,我想生成一个传递一些自定义参数的link_toURL。也就是说,有一个articles_path(www.my_web_site_name.com/articles)我想生成如下内容:link_to'Samplelinktitle',...#HereIshouldimplementthecode#=>'http://www.my_web_site_name.com/articles?param1=value1¶m2=value2&...我如何编写link_to语句“alàRubyonRailsWay”以实现该目的?如果我想通过传递一些

  3. ruby-on-rails - 如何在 Rails 3 中创建自定义脚手架生成器? - 2

    有这些railscast。http://railscasts.com/episodes/218-making-generators-in-rails-3有了这个,你就会知道如何创建样式表和脚手架生成器。http://railscasts.com/episodes/216-generators-in-rails-3通过这个,您可以了解如何添加一些文件来修改脚手架View。我想把两者结合起来。我想创建一个生成器,它也可以创建脚手架View。有点像RyanBates漂亮的生成器或web_app_themegem(https://github.com/pilu/web-app-theme)。我

  4. ruby-on-rails - 事件管理员日期过滤器日期格式自定义 - 2

    是否有简单的方法来更改默认ISO格式(yyyy-mm-dd)的ActiveAdmin日期过滤器显示格式? 最佳答案 您可以像这样为日期选择器提供额外的选项,而不是覆盖js:=f.input:my_date,as::datepicker,datepicker_options:{dateFormat:"mm/dd/yy"} 关于ruby-on-rails-事件管理员日期过滤器日期格式自定义,我们在StackOverflow上找到一个类似的问题: https://s

  5. ruby-on-rails - 从应用程序中自定义文件夹内的命名空间自动加载 - 2

    我们目前正在为ROR3.2开发自定义cms引擎。在这个过程中,我们希望成为我们的rails应用程序中的一等公民的几个类类型起源,这意味着它们应该驻留在应用程序的app文件夹下,它是插件。目前我们有以下类型:数据源数据类型查看我在app文件夹下创建了多个目录来保存这些:应用/数据源应用/数据类型应用/View更多类型将随之而来,我有点担心应用程序文件夹被这么多目录污染。因此,我想将它们移动到一个子目录/模块中,该子目录/模块包含cms定义的所有类型。所有类都应位于MyCms命名空间内,目录布局应如下所示:应用程序/my_cms/data_source应用程序/my_cms/data_ty

  6. ruby-on-rails - Rails - 使用/自定义 URL : '/dashboard' 指定根路径 - 2

    如何使此根路径转到:“/dashboard”而不仅仅是http://example.com?root:to=>'dashboard#index',:constraints=>lambda{|req|!req.session[:user_id].blank?} 最佳答案 您可以通过以下方式实现:root:to=>redirect('/dashboard')match'/dashboard',:to=>"dashboard#index",:constraints=>lambda{|req|!req.session[:user_id].b

  7. ruby-on-rails - 在 heroku 的 .fonts 文件夹中包含自定义字体,似乎无法识别它们 - 2

    Heroku支持人员告诉我,为了在我的Web应用程序中使用自定义字体(未安装在系统中,您可以在bash控制台中使用fc-list查看已安装的字体)我必须部署一个包含所有字体的.fonts文件夹里面的字体。问题是我不知道该怎么做。我的意思是,我不知道文件名是否必须遵循heroku的任何特殊模式,或者我必须在我的代码中做一些事情来考虑这种字体,或者如果我将它包含在文件夹中它是自动的......事实是,我尝试以不同的方式更改字体的文件名,但根本没有使用该字体。为了提供更多详细信息,我们使用字体的过程是将PDF转换为图像,更具体地说,使用rghostgem。并且最终图像根本不使用自定义字体。在

  8. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  9. ruby-on-rails - Heroku 吃掉了我的自定义 HTTP header - 2

    我正在使用Heroku(heroku.com)来部署我的Rails应用程序,并且正在构建一个iPhone客户端来与之交互。我的目的是将手机的唯一设备标识符作为HTTPheader传递给应用程序以进行身份​​验证。当我在本地测试时,我的header通过得很好,但在Heroku上它似乎去掉了我的自定义header。我用ruby​​脚本验证:url=URI.parse('http://#{myapp}.heroku.com/')#url=URI.parse('http://localhost:3000/')req=Net::HTTP::Post.new(url.path)#boguspara

  10. ruby-on-rails - 事件管理员和自定义方法 - 2

    这是我在ActiveAdmin中的自定义页面ActiveAdmin.register_page"Settings"doaction_itemdolink_to('Importprojects','settings/importprojects')endcontentdopara"Text"endcontrollerdodefimportprojectssystem"rakedataspider:import_projects_ninja"para"OK"endendend我想做的是,当我单击“导入项目”按钮时,我想在Controller中执行rake任务。但是我无法访问该方法。可能是什

随机推荐