Използване на Retrofit за достъп до JSON масиви

Мислех, че разбирам как да направя това, но очевидно не. Имам моя API от Flickr, който започва така:

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

Много ясно, че изглежда пропускам смисъла, но не съм сигурен как да получа информацията..


person K20GH    schedule 16.02.2014    source източник
comment
Разбрахте ли го накрая?   -  person Lion789    schedule 18.03.2014


Отговори (5)


Бърз преглед на документите на Retrofit казва, че използва Gson за конвертиране на JSON в Java класове. Това означава, че имате нужда от йерархия на класове в Java, която съответства на JSON. Вашият... не.

Върнатият JSON е обект с едно поле „снимки“, което съдържа обект;

{ "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 по подходящ начин.

person Brian Roach    schedule 16.02.2014
comment
Добре, така че редактирах първата си публикация, за да отразя промените. Как да извикам всяка променлива в друг клас? Използвам следното като пример за основа: github.com/romannurik/muzei/tree/master/example-source-500px/ - person K20GH; 16.02.2014
comment
@Brian Roach отделни ли са тези класове или могат да бъдат във файл на клас и ако е един файл, какво ще бъде името на файла... photosResponse или Photos? Всъщност се опитвам да използвам този формат за масив. - person Lion789; 18.03.2014
comment
Има ли по-лесен начин за усукване на JSON структурата, за да картографирате клас, който вече имате? напр. в този пример, ако всичко, което ме интересува, е ключът за снимки, не искам да си губя времето в дефиниране на обект, съответстващ на структурата, ако не планирам да го използвам по този начин. Бих предпочел да дефинирам картографиране, като посочвам, че искам само собствеността на снимките. - person Ted Avery; 03.06.2014
comment
Това добра практика ли е? Искам да кажа, че не разбирам смисъла на използването на Retrofit, за да избегна анализа на JSON, но въпреки това трябва да създавам като класове, тъй като вложените обекти/масиви са в JSON. Ако анализирате JSON ръчно, няма да се налага да създавате клас PhotosResponse или клас Photos, просто създайте клас Photo и вземете масива със снимките, като използвате gson за анализиране на класа Photo. Има ли някакъв начин да получите масива от снимки, без да създавате толкова много класове с помощта на Retrofit? Благодаря - person FOMDeveloper; 27.05.2015
comment
Мисля, че използването на преоборудване е нещо повече от избягване на самостоятелен анализ на JSON. Първо, преоборудването е много по-бързо от залповото или асинхронното задание. Според моя личен опит преоборудването прави извършването на сервизни обаждания доста плавен процес. Осигурява много опростен механизъм за обратно извикване. Позволява ви динамично да замествате пътен сегмент, POST, GET и т.н. и т.н. И когато имате приложения, които правят повече от 50 различни повиквания, използвайки преоборудване, класът изглежда наистина прост и чист, което прави манипулирането на крайните точки наистина лесно и безболезнено. И можете да използвате модернизация в съчетание с RxJava. Мога да продължа да лутам тук. - person user2511882; 29.05.2015
comment
@FOMDeveloper: Два допълнителни класа са малка цена за следване на стандартен начин за десериализация на JSON. Групирайте вашите транспортни класове в отделен транспортен пакет и скоро ще забравите, че съществуват. Засягам концепцията за транспортни класове в тази публикация в блога: nilzorblog .com/2015/04/dont-forget-view-model.html - person Nilzor; 03.06.2015

Предлагам да използвате http://www.jsonschema2pojo.org. Можете да поставите своя JSON и той ще генерира POJO вместо вас.

Това трябва да свърши работа.

person LaSombra    schedule 22.04.2014
comment
Готино. Използвах GSON изхода като ръководство за моите интерфейси в Retrofit. - person Ryan R; 22.10.2014
comment
Страхотно! Прави POJO буркани! Gr8! - person berserk; 04.12.2014

Приетият отговор е правилен, но изисква изграждане на клас 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);

    }

}

Сега създаваме нашия персонализиран gson обект за RestAdapter на Retrofit:

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

По този начин получаваме обекта Photos, без да се налага да създаваме клас PhotosResponse. Можем да го използваме така:

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


    }
});
person FOMDeveloper    schedule 03.06.2015
comment
Това изобщо не е общо. Смисълът на използването на Retrofit е да можете да поискате няколко API върху една дефинирана крайна точка. Какъв е смисълът да се губи способността за независим анализ на отговор? - person Gomino; 03.06.2015
comment
Съгласен съм. Просто исках да знам колко добра практика е създаването на класове като вложени обекти/масиви в Json. Вероятно този подход ще пасне по-добре на огромен Json файл, където просто искате обект от него, вместо за малък Json. Както и да е, този отговор дава решение на първоначалния въпрос, използвайки модернизация, без да се налага да създавате коренния клас. @Nilzor направи добра гледна точка с коментара си. Може би си струва да създадете всички тези транспортни класове и да ги групирате в пакет. - person FOMDeveloper; 03.06.2015
comment
Тогава какво ще кажете за моя отговор? Как не отговаря на OP въпроса? - person Gomino; 03.06.2015
comment
и как можем да итерираме през масива от снимки в успешен метод за обратно извикване на Retrofit? - person tsiro; 13.11.2015
comment
Получавам не мога да разреша символ GsonConverter - person Narendra Singh; 01.11.2016

Трябва да имате директен достъп до com.google.gson.JsonObject от Retrofit и достъп до всяко поле, което искате. Така че, ако се интересувате само от фото масива, нещо подобно трябва да работи:

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

Разбира се, цялата проверка на разума е оставена на вас.

person Gomino    schedule 03.06.2015
comment
Благодаря ви за отговора. Можете ли да добавите обяснение на втория ред. Това обаждане за модернизация ли е? Къде отиват блоковете за успех и провал? - person FOMDeveloper; 03.06.2015
comment
Не, от Gson е. Първо търся избраното поле в дървото JsonObject, след което ръчно конвертирам json низа обратно в списък от вашия персонализиран тип с помощта на Gson TypeToken. Какво имате предвид под блокиране на успех и провал? Използвате Retrofit синхронно, така че просто се опитайте да хванете RetrofitError около вашето API извикване - person Gomino; 03.06.2015

Тъй като вече има няколко отговора, които можете да използвате. Но според мен използвайте това. Направете клас снимки с всички променливи, дадени в обекта снимки и създайте сетер за получаване на всички и също така създайте клас снимка, който ще съдържа списъка със снимки и също така създайте сетер за получаване на този списък отстрани на класа Снимки. По-долу е дадения код.

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.
    }
person Vid    schedule 03.06.2015