37

I thought I understood how to do this, but obviously not. I have my API from Flickr, which begins like so:

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

Now the information I need to get is from within the photo array, so what I have been trying to do is:

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

Very clear that I seem to be missing the point, however I am unsure of how to get the information..

1
  • Did you figure it out at the end? Commented Mar 18, 2014 at 10:43

5 Answers 5

46

I would suggest using http://www.jsonschema2pojo.org. You can paste your JSON and it will generate the POJOs for you.

That should do the trick.

Sign up to request clarification or add additional context in comments.

2 Comments

Cool. I used the GSON output as a guide for my interfaces in Retrofit.
Awesome! It makes POJO jars! Gr8!
42

A quick look at Retrofit's docs says it uses Gson to convert JSON to Java classes. This means you need a class hierarchy in Java that matches the JSON. Yours ... doesn't.

The returned JSON is an object with a single field "photos" that holds an object;

{ "photos" : { ... } }

So, your top level class would be a Java class with a single field:

public class PhotosResponse {
    private Photos photos;

    // getter/setter
}

And that Photos type would be another class that matches the JSON for the object that field contains:

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

So you'd have:

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

    // getters / setters
}

And then you'd create a Photo class to match the structure of the object in that inner array. Gson will then map the returned JSON appropriately.

6 Comments

Ok, so I have edited my first post to reflect the changes. How would I go about calling each variable in another class? I am using the following as an example to base off: github.com/romannurik/muzei/tree/master/example-source-500px/…
@Brian Roach are these classes separate or can they be in on class file and if one file, what would be the name of the file... photosResponse or Photos? I am actually trying to use this format for an array.
Is there an easier way of twisting the JSON structure to map a class you already have? e.g. in this example, if all I care about is the photos key, I don't want to waste my time defining an object matching the structure if I don't plan to use it that way. I would rather define a mapping stating that I only want the photos property.
Is this a good practice? I mean, I dont understand the point of using Retrofit to avoid parse JSON, but however I have to create as classes as nested objects/arrays are in the JSON. If you would parse the JSON manually you wouldn't have to create PhotosResponse class or Photos class, just create a Photo class and get the array with the Photos using gson to parse the Photo class. Are there any way to get the array of Photos without create so many classes using Retrofit? Thanks
@FOMDeveloper: Two extra classes is a small price to pay for following a standard way of deserializing JSON. Group your transport classes in a separate "transport"-package and you'll soon forget they exist. I touch upon the concept of transport classes in this blog post: nilzorblog.com/2015/04/dont-forget-view-model.html
|
11
+50

The accepted answer is correct but it requires building a PhotoResponse class which only has one object in it. This the following solution, we only need to create the Photos class and some sterilization.

We create a 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);

    }

}

Now we create our custom gson object to Retrofit's RestAdapter:

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

And then we set the converter in the retrofit adapter:

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

And the interface would look like this:

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

This way we get the Photos object without having to create PhotosResponse class. We can use it like this:

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


    }
});

5 Comments

This is not generic at all. The point of using Retrofit, is to be able to request several api overone defined endpoint. What is the point of loosing the ability to parse answer independently ?
I agree. I just wanted to know how good practice is create as classes as nested objects/arrays are in a Json. Probably this approach would fit better in a huge Json file where you just want an object from it, instead for a small Json. Anyway this answer gives a solution to the original question using retrofit without having to create the root class. @Nilzor make a good point with his comment. Maybe it worth create all these transport classes and group them in a package.
Then what about my answer ? How does it not answer the OP question ?
and how can we iterate through array of "photo" in success callback method of Retrofit?
I am getting cannot resolve symbol GsonConverter
2

You should be able to directly access the com.google.gson.JsonObject from Retrofit, and access whatever field you would like. So if you are only interested in the Photo array something like this should works:

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

And when you actually call the service just run the JsonObject to get what you want:

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

Of course all the sanity check are left to you.

2 Comments

Thanks for your response. Can you please add an explanation on the second line. Is that a retrofit call? Where do the success and fail blocks go?
Nope it's from Gson. I first search the JsonObject tree for the selected field then I manually convert the json string back to a list of your custom type using Gson TypeToken. What do you mean by success and fail block ? You are using Retrofit synchronously so simply try to catch a RetrofitError around your api call
1

As already few answers are above which you can use. But as per my view use this. Make a photo class with all the variables given in photos object and create getter setter of all and also create a class of photo with will hold the list of photos and also create getter setter of this list in side the Photos class. Below is the code given.

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.