3

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

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

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

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

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

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<Site>集合,这同样适用于其他对象,例如Question,Answer等等。

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

我推断,公共包装器中有一个未返回的字段,称为type 这里的想法是稍后作弊......而且,由于它不是过滤器的一部分,这意味着必须通过创建一个新过滤器/filter/createAPI 并将其作为调用的一部分应用于 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){
   }
});

在研究了在 SO 上回答TypeAdapterFactory的Gson 的习俗之后,我是否在这里遗漏了一些东西,这就是我到目前为止所拥有的。

创建了一个名为的类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 对象,同时创建一个该类型的 List ,Site对于返回的每个 API 对象,它确实感觉很麻烦且冗长。

我这样做是错误的方式还是我对 Retrofit 库的期望有点太高了?

4

1 回答 1

3

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

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

解决方案相当简单。

CommonSEWrapper.java

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

然后申请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 的魔法内部混为一谈。

希望这能帮助那些发现自己处于我经历过的类似困境的人。

享受。

于 2014-01-23T21:14:11.803 回答