2014-02-16 2 views
37

Я думал, что понял, как это сделать, но, очевидно, нет. У меня есть API от Flickr, который начинается так:Использование Retrofit для доступа к массивам JSON

jsonFlickrApi({ 
    "photos":{ 
     "page":1, 
     "pages":10, 
     "perpage":100, 
     "total":1000, 
     "photo":[ 
     { 
      "id":"12567883725", 
      "owner":"[email protected]", 
      "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; 
    } 
} 

Очень ясно, что мне, кажется, не хватает смысла, однако я не знаю, как получить информацию.

+0

Вы поняли это в конце? – Lion789

ответ

40

Быстрый взгляд на документы 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 соответствующим образом.

+0

Итак, я отредактировал свое первое сообщение, чтобы отразить изменения. Как мне обратиться к каждой переменной в другом классе? В качестве примера я использую следующее: https://github.com/romannurik/muzei/tree/master/example-source-500px/src/main/java/com/example/muzei/examplesource500px – K20GH

+0

@Brian Роуч - эти классы отдельно или могут быть в файле класса, и если один файл, каким будет имя файла ... photosResponse или Photos? Я действительно пытаюсь использовать этот формат для массива. – Lion789

+1

Есть ли более простой способ скрутить структуру JSON для отображения класса, который у вас уже есть? например в этом примере, если все, о чем я забочусь, это ключ фотографий, я не хочу тратить свое время на определение объекта, соответствующего структуре, если я не планирую использовать его таким образом. Я бы скорее определил отображение, в котором утверждается, что мне нужно только свойство фотографии. –

47

Я предлагаю использовать http://www.jsonschema2pojo.org. Вы можете вставить свой JSON, и он будет генерировать POJO для вас.

Это должно сделать трюк.

+0

Прохладный. Я использовал вывод GSON в качестве руководства для своих интерфейсов в «Дооснащении». –

+1

это большое спасибо! – safari

+1

Удивительный! Это делает баночки POJO! Супер! – berserk

2

Вы должны иметь возможность прямого доступа к 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()); 

Конечно все проверки здравомыслия оставлены вам.

+0

Спасибо за ваш ответ. Можете ли вы добавить объяснение во вторую строку. Это переоборудование?Где идут блоки успеха и отказа? – FOMDeveloper

+0

Нет, это от Гссона. Сначала я ищу дерево JsonObject для выбранного поля, а затем вручную преобразую строку json в список вашего пользовательского типа, используя Gson TypeToken. Что вы подразумеваете под удачей и неудачей? Вы используете Retrofit синхронно, поэтому попробуйте поймать RetrofitError вокруг вашего вызова api. – Gomino

10

Принимаемый ответ правильный, но для этого требуется создать класс 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 ДООСНАСТКЕ в:

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


    } 
}); 
+0

Это не общее. Точка использования Retrofit - это возможность запросить несколько api overone определенных конечных точек. В чем смысл потерять способность самостоятельно анализировать ответ? – Gomino

+0

Согласен. Я просто хотел знать, как хорошая практика создается как классы, так как вложенные объекты/массивы находятся в Json. Вероятно, этот подход подойдет лучше в огромном Json-файле, где вы просто хотите получить от него объект, вместо этого для небольшого Json. Во всяком случае, этот ответ дает решение исходного вопроса с использованием модификации без необходимости создания корневого класса. @ Нилзор делает хорошую оценку своим комментарием. Возможно, стоит создать все эти транспортные классы и сгруппировать их в пакет. – FOMDeveloper

+0

Тогда как насчет моего ответа? Как это не отвечает на вопрос ОП? – Gomino

0

Поскольку уже несколько ответов выше, которые вы можете использовать. Но, по моему мнению, используйте это. Сделайте класс фотографии со всеми переменными, заданными в объекте фотографий, и создайте сеттер для всех, а также создайте класс фотографии, в котором будет сохранен список фотографий, а также создайте сеттер-получатель этого списка в классе Photos. Ниже приведен код.

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