3

I'm trying to build an app using an external API (I have no control over the JSON that I'm getting and can't change it). I can't find how to pares something like that, as it is highly un orthodox in its structure:

{
    "1":{
        "id":1
        "name:"name1"
        "books":[{BOOK},{BOOK},{BOOK}]
    },
    "2":{
        "id":2
        "name:"name2"
        "books":[{BOOK},{BOOK},{BOOK}]
    },
    "3":{
        "id":3
        "name:"name3"
        "books":[{BOOK},{BOOK},{BOOK}]
    },
    "4":{
        "id":4
        "name:"name4"
        "books":[{BOOK},{BOOK},{BOOK}]
    },
    ....
}

A little explanation about the structure: 1. The first level is the categories of the book. 2. In each book in the array there is a field that points to the category 3. As you can see, the id is the same as the key of the category object

The Task: I need to parse this into: 1. All the categories (I've build a Category Class and a CategoryResponse Class with a list of categories) 2. I Need to parse the Array of Books in each category

Category Class:

public class Category {
    @SerializedName("Name")
    private String name;
    @SerializedName("Id")
    private Integer id;
    @SerializedName("OrderKey")
    private Integer orderKey;
    @SerializedName("Books")
    private List<Book> books = new ArrayList<Book>();

    public Category(String name, Integer id, Integer orderKey, List<Book> books) {
        this.name = name;
        this.id = id;
        this.orderKey = orderKey;
        this.books = books;
    }

    public String getName() {
       return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public List<Book> getBooks() {
        return books;
    }

    public void setBooks(List<Book> books) {
       this.books = books;
    }
}

CategoryResponse Class:

public class CategoryResponse {
    @SerializedName("")
    private List<Category> categories;

    public List<Category> getCategories() {
        return categories;
    }

    public void setCategories(List<Category> categories) {
        this.categories = categories;
    }
}

I have a book class also.... This is not the problem.

ApiClient Class:

public static Retrofit getClient() {
    if(retrofit == null) {
        retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }
    return retrofit;
}

ApiInterface:

public interface ApiInterface {
    @GET("Books/GetBooks")
    Call<CategoryResponse> getCategories();
}

The Fragment for the Categories and Books (In onCreateView method):

ApiInterface apiService = ApiClient.getClient().create(ApiInterface.class);

    Call<CategoryResponse> call = apiService.getCategories();
    call.enqueue(new Callback<CategoryResponse>() {
        @Override
        public void onResponse(Call<CategoryResponse> call, Response<CategoryResponse> response) {
            categories = response.body().getCategories();
            Log.d(TAG, "Number of categories recieved: " + categories.size());
        }

        @Override
        public void onFailure(Call<CategoryResponse> call, Throwable t) {
            Log.e(TAG, t.toString());
        }
    });

    return inflater.inflate(R.layout.fragment_books, container, false);
}

As you can see, There are two problems over here: 1. I don't know what to wrote in @SerializedName in CategoryResponse 2. The response comes back 200 (Good) but with no categories as there is not way i can find how to pares the JSON with no root element and not a list, just an object with objects but each objects is of the same type (a Category with fields and an array of books)

I can't find the solution in Android for this (In IOS it was very simple but just can't find how to do it in Android)

The App crashes on the Log for the success response because categories is null (of course it is, i know it is wrong, as there is no root to parse from, but can't fix it)

10x

Erez
  • 1,933
  • 5
  • 29
  • 56
  • Can you show us the api response or link from where you are getting it – Rakshit Nawani Jan 06 '17 at 22:26
  • I've added the response, but it is shows that i made a good request and got response for that, but there is not categories (I know, i need to parse the object as shown in the begging of my question) – Erez Jan 06 '17 at 22:38
  • I'm thinking about going to the basics and not using retrofit or GSON at all, getting the JSON, looping over it and and parsing each object like it the old days before all the helpers. I don't want to go there, but i can see a way around it. – Erez Jan 06 '17 at 22:44
  • I'm no expert on Gson but a Json object like the one above, doesn't seem to be a List of objects but rather a Map of objects. Maybe it is worth it to change the `List` and see if it can parse it. Also is the number of categories fixed and known in advance? – Lukasz Jan 06 '17 at 22:55
  • I know it is not an array, i need to take the object and parse it to an a list of categories. That i was i meant when i said it is unorthodox JSON. Usually there is an array if the objects are the same, but here they went with a deferent approach. that is the problem. And that is why i thinking that retrofit and GSON will not work for me in this case. I'm asking here and not jumping to do that because it is not the only call and i want to make life easier, but eventually if there will be no answer i will do i manually and be done with it. 10x – Erez Jan 06 '17 at 23:00
  • Getting the response, looping over the objects and for each object deserializing it. I had the same issue, so i went with the basic approach and it was very easy, but every one is saying to use retrofit and GSON so i tried to go with that. As i said before, if i will not find an answer i will go with that, but i would like to learn if there is a better, new way to do it – Erez Jan 06 '17 at 23:13
  • 1
    OK so try the same but not give up retrofit, just use `response.getBody().toString()` in `onResponse` then convert to jsonobject and loop over the objects – Leśniakiewicz Jan 06 '17 at 23:51
  • Loop over a string??? Maybe i wasn't clear. i had a dictionary of String:AnyObject in Swift that i looped over, not a string... How can you loop over a string? Sorry of the question, maybe i don't understand what you meant ... – Erez Jan 06 '17 at 23:57
  • 1
    I said 'convert to jsonobject' `JSONObject obj = new JSONObject(response.getBody().toString())` then iterate http://stackoverflow.com/questions/9151619/how-to-iterate-over-a-jsonobject – Leśniakiewicz Jan 07 '17 at 00:01
  • Your right, didn't see it, sorry :-) – Erez Jan 07 '17 at 00:08
  • Still not working, maybe my retrofit call is wrong because it throws an exception now when i try to son object. it should be wrong because i am using a CategoryResponse generic on it... so I'm guessing there is not meaning for that. But thanks, that might me a good direction to go – Erez Jan 07 '17 at 00:17
  • i had a surprisingly similar structure in one of my projects and i would really really recommend to you to recieve the whole response as an categories object that extends Hashmap. That CategoryObj would be your Category class. Than it is really easy to parse your Hashmap to a string list of your categories. – Malik Jan 07 '17 at 17:20
  • the structure would be like this: http://pastebin.com/LedD6Zqp – Malik Jan 07 '17 at 17:23
  • 10x Malik, but the code you showed me made more errors then helped. I need to research this issue more i guess. Still, every one ignores the fact that all the examples in retrofit show that i need to give "@SerializedName()" the name of the key to connect and here i don't have a key to give so i am not getting any data in the result, it returns 200 but empty i guess (null) and the other thing is that the code that you gave didn't even compile (Hashmap should be HashMap? and need to do the CategoryMap in deferent file or not) so i don't know what to do. I know that you tried to help, so 10x... – Erez Jan 07 '17 at 21:41
  • It looks to me like the following should work: Data - top level class that will be the return value of your retrofit call Inside Data you would have a HashMap categories; – FrankR Jan 18 '17 at 16:05

0 Answers0