【问题标题】:Using Retrofit to access JSON arrays使用 Retrofit 访问 JSON 数组
【发布时间】:2014-03-15 22:02:29
【问题描述】:

我以为我知道如何做到这一点,但显然不是。我有来自 Flickr 的 API,开头是这样的:

jsonFlickrApi({
   "photos":{
      "page":1,
      "pages":10,
      "perpage":100,
      "total":1000,
      "photo":[
         {
            "id":"12567883725",
            "owner":"74574845@N05",
            "secret":"a7431762dd",
            "server":"7458",
            "farm":8,
            "title":"",
            "ispublic":1,
            "isfriend":0,
            "isfamily":0,
            "url_l":"http:\/\/farm8.staticflickr.com\/7458\/12567883725_a7431762dd_b.jpg",
            "height_l":"683",
            "width_l":"1024"
         }

现在我需要从照片数组中获取信息,所以我一直在尝试做的是:

interface ArtService {

    @GET("/services/rest/?method=flickr.photos.getRecent&extras=url_l&owner_name&format=json")
    PhotosResponse getPhotos();

    public class PhotosResponse {
        Photos photos;
    }

    public class Photos {
        List<Arraz> photo;
    }

    public class Arraz {
        int id;
        String title;
        String owner;
        String url_l;
    }
}

很清楚我似乎没有抓住重点,但是我不确定如何获取信息..

【问题讨论】:

  • 你最后搞清楚了吗?

标签: java android retrofit


【解决方案1】:

快速浏览一下 Retrofit 的文档说它使用 Gson 将 JSON 转换为 Java 类。这意味着您需要一个与 JSON 匹配的 Java 类层次结构。你的……没有。

返回的 JSON 是一个包含单个字段“photos”的对象;

{ "photos" : { ... } }

因此,您的顶级类将是具有单个字段的 Java 类:

public class PhotosResponse {
    private Photos photos;

    // getter/setter
}

Photos 类型将是另一个与该字段包含的对象的 JSON 匹配的类:

{ "page":1, "pages":10, ... }

所以你有:

public class Photos {
    private int page;
    private int pages;
    private int perpage'
    private int total;
    private List<Photo> photo;

    // getters / setters
}

然后您将创建一个Photo 类来匹配该内部数组中对象的结构。然后,Gson 将适当地映射返回的 JSON。

【讨论】:

  • 好的,所以我编辑了我的第一篇文章以反映更改。我将如何去调用另一个类中的每个变量?我以以下示例为基础:github.com/romannurik/muzei/tree/master/example-source-500px/…
  • @Brian Roach 这些类是分开的,还是它们可以在类文件中,如果是一个文件,文件的名称是什么...photosResponse 或 Photos?我实际上是在尝试将这种格式用于数组。
  • 有没有更简单的方法来扭曲 JSON 结构来映射你已经拥有的类?例如在这个例子中,如果我只关心照片键,如果我不打算以这种方式使用它,我不想浪费我的时间来定义一个与结构匹配的对象。我宁愿定义一个映射,说明我只想要 photos 属性。
  • 这是一个好习惯吗?我的意思是,我不明白使用 Retrofit 来避免解析 JSON 的意义,但是我必须创建作为嵌套对象/数组在 JSON 中的类。如果您要手动解析 JSON,则不必创建 PhotosResponse 类或 Photos 类,只需创建一个 Photo 类并使用 gson 获取包含 Photos 的数组来解析 Photo 类。有没有办法在不使用 Retrofit 创建这么多类的情况下获取照片数组?谢谢
  • @FOMDeveloper:按照标准的反序列化 JSON 方式,两个额外的类是很小的代价。将您的传输类分组到一个单独的“传输”包中,您很快就会忘记它们的存在。我在这篇博文中谈到了传输类的概念:nilzorblog.com/2015/04/dont-forget-view-model.html
【解决方案2】:

我建议使用http://www.jsonschema2pojo.org。您可以粘贴您的 JSON,它会为您生成 POJO。

这应该可以解决问题。

【讨论】:

  • 酷。我在 Retrofit 中使用 GSON 输出作为我的接口的指南。
  • 太棒了!它可以制作 POJO 罐子! Gr8!
【解决方案3】:

您应该能够从 Retrofit 直接访问 com.google.gson.JsonObject,并访问您想要的任何字段。 因此,如果您只对 Photo 数组感兴趣,那么这样的方法应该可以:

interface ArtService {
    @GET("/services/rest/?method=flickr.photos.getRecent&extras=url_l&owner_name&format=json")
    JsonObject getPhotos();

    public class Photo {
         int id;
         String title;
         String owner;
         String url_l;
    }
}

当您实际调用服务时,只需运行 JsonObject 即可获得所需内容:

    JsonObject json = mRestClient.getArtService().getPhotos();
    List<ArtService.Photo> photos = new Gson().fromJson(json.getAsJsonObject("photos").get("photo").toString(), new TypeToken<List<ArtService.Photo>>() {}.getType());

当然,所有的健全性检查都留给你。

【讨论】:

  • 感谢您的回复。您能否在第二行添加解释。那是改装电话吗?成功和失败块在哪里?
  • 不,它来自 Gson。我首先在 JsonObject 树中搜索所选字段,然后使用 Gson TypeToken 手动将 json 字符串转换回您的自定义类型列表。成功和失败块是什么意思?您正在同步使用 Retrofit,因此只需尝试在您的 api 调用周围捕获一个 RetrofitError
【解决方案4】:

接受的答案是正确的,但它需要构建一个 PhotoResponse 类,其中只有一个对象。这下面的解决方案,我们只需要创建 Photos 类并进行一些消毒。

我们创建一个 JsonDeserializer:

class PhotosDeserializer implements JsonDeserializer<Photos>
{
    @Override
    public Photos deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {

        JsonElement content = json.getAsJsonObject().get("photos");

        return new Gson().fromJson(content, Photos.class);

    }

}

现在我们为 Retrofit 的 RestAdapter 创建自定义 gson 对象:

    Gson gson = new GsonBuilder()
                    .registerTypeAdapter(Photos.class, new PhotosDeserializer())
                    .create();

然后我们在改装适配器中设置转换器:

 RestAdapter restAdapter = new RestAdapter.Builder()
                                            .setEndpoint(ArtService.ENDPOINT)
                                            .setConverter(new GsonConverter(gson))
                                            .build();

界面看起来像这样:

@GET("/?method="+METHOD_GET_RECENT+"&api_key="+API_KEY+"&format=json&nojsoncallback=1&extras="+EXTRAS_URL)
public void getPhotos(Callback<Photos> response);

这样我们无需创建 PhotosResponse 类即可获得 Photos 对象。我们可以这样使用它:

ArtService artService = restAdapter.create(ArtService.class);
artService.getPhotos(new Callback<Photos>() {
    @Override
    public void success(Photos photos, Response response) {

        // Array of photos accessing by photos.photo

   }

   @Override
   public void failure(RetrofitError error) {


    }
});

【讨论】:

  • 这根本不是通用的。使用 Retrofit 的目的是能够请求多个 api overone 定义的端点。失去独立解析答案的能力有什么意义?
  • 我同意。我只是想知道作为嵌套对象/数组在 Json 中的类创建的良好实践。可能这种方法更适合一个巨大的 Json 文件,你只需要一个对象,而不是一个小的 Json。无论如何,这个答案使用改造为原始问题提供了解决方案,而无需创建根类。 @Nilzor 对他的评论提出了一个很好的观点。也许值得创建所有这些传输类并将它们分组到一个包中。
  • 那我的回答呢?它怎么不回答 OP 问题?
  • 我们如何在Retrofit的成功回调方法中遍历“照片”数组?
  • 我得到 无法解析符号 GsonConverter
【解决方案5】:

由于您可以使用的答案已经很少。但根据我的观点,使用这个。 使用照片对象中给出的所有变量创建一个照片类,并创建所有的getter setter,并创建一个包含照片列表的照片类,并在Photos类中创建此列表的getter setter。下面是给出的代码。

public static class Photos {

        @JsonProperty("page")
        private double page;
        @JsonProperty("pages")
        private double pages;
        @JsonProperty("perpage")
        private double perpage;
        @JsonProperty("total")
        private double total;

        @JsonProperty("photo")
        private List<Photo> photo;


        public double getPage() {
            return page;
        }

        public void setPage(double page) {
            this.page = page;
        }

        public double getPages() {
            return pages;
        }

        public void setPages(double pages) {
            this.pages = pages;
        }

        public double getPerpage() {
            return perpage;
        }

        public void setPerpage(double perpage) {
            this.perpage = perpage;
        }

        public double getTotal() {
            return total;
        }

        public void setTotal(double total) {
            this.total = total;
        }
    }

    public static class Photo {
        // refer first class and declare all variable of photo array and generate getter setter.
    }

【讨论】:

    猜你喜欢
    • 2015-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-10
    • 2021-01-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多