【问题标题】:StackExchange API handling with Retrofit使用 Retrofit 处理 StackExchange API
【发布时间】:2014-02-11 12:18:20
【问题描述】:

我正在编写一个简洁的 Android 应用程序来与 API 交互,它遵循 Google Android 用户体验。

应用程序将使用 Retrofit(使 REST-api 机制更容易)。

现在,我已经创建了对应于每个 API 调用的 Java 类。

例如,Site.java 类以这种方式处理 /sites API:

为了简洁起见,这是浓缩的:

public class Site{
    @SerializedName("api_site_parameter") String mApiSiteParameter = "";
    // snip
}

接口以这种方式连接起来,称为ISites.java

public interface ISites{
    @GET("/sites")
    public void getAllSites(@Query("filter") String filterName, 
                            Callback<List<Site>> cbAllSites);
}

最大的问题是,由于使用了通用包装器,我怎样才能让 Retrofit 返回一个 List&lt;Site&gt; 集合,这同样适用于其他对象,例如 QuestionAnswer 等等。

在 Chrome 下使用 Postman 扩展进行测试后,我观察到以下情况,这是一个绊脚石,无论使用哪个 REST-api,都会在响应中返回公共包装器,但是 JSON 中的 items 字段输出,在这种情况下包含Site 的数组。

我推断,通用包装器中有一个字段没有返回,称为type 这里的想法是稍后作弊......而且,因为它不是一部分过滤器,这意味着必须通过 /filter/create API 创建一个新过滤器,并将其作为调用的一部分应用于 Retrofit 的 RestAdapter 调用,如下所示:

RestAdapter ra = new RestAdapter.Builder()
   .setServer("http://api.stackexchange.com/2.1")
   .setLogLevel(LogLevel.FULL)
   .build();
ISites sites = ra.create(ISites.class);
sites.getAllSites("my_commonwrapper_filter", Callback<Site>(){
   @Override
   public void failure(RetrofitError argRetrofitError){
   }
   @Override
   public void success(List<Site> sites, Response response){
   }
});

在研究了 Gson 的自定义 TypeAdapterFactoryanswered here on SO 之后,我是否在这里遗漏了什么,这是我目前所拥有的。

创建了一个名为 SEWrapper.java 的类,如下所示:

public class SEWrapper{
    @SerializedName("items") List<Class ?> mListItems;
    @SerializedName("type") String mJsonObjType;
}

围绕 Site.java 源更改为使用 SEWrapper 而不是 Site,并修改了 REST 调用。

这段代码改编自 StackOverflow 上的问题,

private class SEWrapperTypeAdapterFactory extends CustomizedTypeAdapterFactory<SEWrapper> {
    private SEWrapperTypeAdapterFactory() { super(SEWrapper.class); }
    @Override
    protected void beforeWrite(SEWrapper source, JsonElement toSerialize) {
       // Ignored for now as all this is Read Only operation!
    }

    @Override
    protected void afterRead(JsonElement deserialized) {
       String typeOfJsonObj = deserialized.getAsJsonObject().get("type").getAsString();
       if (typeOfJsonObj.equalsIgnoreCase("site")){
          JsonArray jSiteArray = deserialized.getAsJsonObject().get("items").getAsJsonArray();
          // Convert that to List<Site> type which I cannot figure out
       }
   }

我不知道如何绕过这个绊脚石。

理论上,我可以重写它以返回 Retrofit 的 Response 对象并手动解析每个 JSON 对象以及创建该类型的列表 Site,对于每个 API 来说确实感觉很麻烦且冗长返回的对象。

我这样做是错误的方式,还是我对 Retrofit 库的期望过高?

【问题讨论】:

    标签: java android json api retrofit


    【解决方案1】:

    当一个灯泡瞬间熄灭时,这个问题的答案非常简单。

    我意识到项目列表可以引用任何对象,具体取决于对 stackexchange.com 站点的 API 调用,所以它意味着一个 type of 类应该当 JSON 的 items 反序列化时使用。

    解决方案相当简单。

    CommonSEWrapper.java

    public class CommonSEWrapper<T>{
       // snip
       @SerializedName("items") List<T> mListItems;
       // snip
    }
    

    然后申请Site 对象,Site 的 REST API 的接口最终会是这样,ISites.java

    public interface ISites{
       @GET("/sites")
        public void getAllSites(@Query("filter") String filterName, 
                                Callback<CommonSEWrapper<Site>> cbAllSites);
    }
    

    最后通过 Retrofit 执行 REST 如下所示:

    RestAdapter ra = new RestAdapter.Builder()
            .setServer("http://api.stackexchange.com/2.1")
            .build();
    ISites sites = ra.create(ISites.class);
    ra.getAllSites("", new Callback<CommonSEWrapper<Site>>(){
       @Override
       public void failure(RetrofitError argRetrofitError){
       }
       @Override
       public void success(CommonSEWrapper<Site> sites, Response response){
           // sites is filled in, just like magic!
       }
    });
    

    干净、优雅、简单,并且不会与 Retrofit 的魔法内部混为一谈。

    希望这将帮助那些发现自己陷入与我经历过类似困境的人。

    享受吧。

    【讨论】:

    • 问题:每次为了获得改造响应,我们必须根据我们的响应创建 CommonSEWrapper.java 类?是这个好办法吗,或者我们可以使用stackoverflow.com/a/26555618/2382964这个答案来一一解析JSON。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-07
    • 2016-07-16
    • 1970-01-01
    • 2020-02-08
    • 2014-10-02
    • 1970-01-01
    • 2014-10-27
    相关资源
    最近更新 更多