1

I am facing some difficulties when getting a JSON String and using it in my Android app. So, I have this class Category2, where I have defined all the fields a category must have:

public class Category2 {

    @SerializedName("_id")
    private String _id;
    @SerializedName("name")
    private String name;
    @SerializedName("tasks")
    private int tasks;

    public Category2(String _id, String name, int tasks) {

        this._id = _id;
        this.name = name;
        this.tasks = tasks;
    }


    public String get_id(){
        return _id;
    }
    public void set_id(String _id){
        this._id = _id;
    }
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
    public int getTasks() {
        return tasks;
    }
    public void setTasks(int tasks){
        this.tasks = tasks;
    }
}

And I have a class called CategoryResponse that is basically a list of category2 that I get from the retrofit request:

public class CategoryResponse {

    private List<Category2> results;

    public List<Category2> getResults() {
        return results;
    }

    public void setResults(List<Category2> results) {
        this.results = results;
    }
}

Generally I deal with JSON strings that have a name for the results even when it's a list of items like:

{"genres":[{"id":28,"name":"Action"},{"id":12,"name":"Adventure"}]}

When the JSON string is like the above I just add a Serialized for the list results above the declaration of the List:

@SerializedName("genres")

And it works just fine when I declare my GET method:

@GET("/v3/projects/{projectId}/categories/")
    Call<CategoryResponse> getProjectCategories(@Query("projectId") String projectId, @Header("Token") String token);

And I call it in my Activity:

categoryService =
        CategoryClient.getClient().create(CategoryService.class);

Call<CategoryResponse> call = categoryService.getProjectCategories(projectId,token);
call.enqueue(new Callback<CategoryResponse>() {
    @Override
    public void onResponse(Call<CategoryResponse> call, Response<CategoryResponse> response) {

        //int statusCode = response.code();
        listCategories = new ArrayList<>();
        listCategories = response.body().getResults();
        System.out.println("Size: " + response.body().getResults().size());
    }

    @Override
    public void onFailure(Call<CategoryResponse> call, Throwable t) {
        // Log error here since request failed
        Log.e(TAG, t.toString());
    }
});

But what I have now for the categories is something like this:

[
    {
        "_id": "59a6b18b2ba5c14bb76f28c8",
        "createdAt": "2017-08-30T12:37:31.885Z",
        "updatedAt": "2017-08-30T12:37:31.885Z",
        "projectId": "598cbc74a1d0e57722ca98d1",
        "companyId": "5602eb7ce49c9cd70409f206",
        "name": "Arquitetura",
        "__v": 0,
        "tasks": 0
    },
    {
        "_id": "59a6b1be2ba5c14bb76f28c9",
        "createdAt": "2017-08-30T12:38:22.407Z",
        "updatedAt": "2017-08-30T12:38:22.407Z",
        "projectId": "598cbc74a1d0e57722ca98d1",
        "companyId": "5602eb7ce49c9cd70409f206",
        "name": "Manutenção",
        "__v": 0,
        "tasks": 0
    }
]

So I cannot serialize the name of the list of results and I am getting null when I call "response.body.GetResults()".

halfer
  • 19,824
  • 17
  • 99
  • 186
Marcos Guimaraes
  • 1,243
  • 4
  • 27
  • 50
  • Try removing `Category2` Copy Constructor. If there is a Copy Constructor available, no default Constructror is called when you make new `Category2` Class objects. Try this and tell me if it works – Tasos Moustakas Aug 31 '17 at 13:55

2 Answers2

5

On your service code just return a Call<ArrayList<Category2>>

@GET("/v3/projects/{projectId}/categories/")
Call<ArrayList<Category2>> getProjectCategories(@Path("projectId") String projectId, @Header("Token") String token);

The parsing will be made correctly by retrofit this way.

EDIT

How you call this service:

categoryService =
        CategoryClient.getClient().create(CategoryService.class);

Call<ArrayList<Category2>> call = categoryService.getProjectCategories(projectId,token);
call.enqueue(new Callback<ArrayList<Category2>>() {
    @Override
    public void onResponse(Call<ArrayList<Category2>> call, Response<ArrayList<Category2>> response) {
        listCategories = response.body();
        System.out.println("Size: " + listCategories.size().toString());
    }

    @Override
    public void onFailure(Call<ArrayList<Category2>> call, Throwable t) {
        // Log error here since request failed
        Log.e(TAG, t.toString());
    }
});
João Magalhães
  • 2,785
  • 2
  • 13
  • 17
  • Thanks for the reply! I try to do the changes that you suggested but I am still getting: attempt to invoke virtual method 'int java.util.ArrayList.size()' on a null object reference. And I know that the result should not come back as null given the token and projectID that I am passing. – Marcos Guimaraes Aug 31 '17 at 12:29
  • could it be because of the retrofit service? You should use the `@Path("projectId")` annotation instead of `@Query("projectId")` when you want to add the parameter to the call path. – João Magalhães Aug 31 '17 at 12:41
  • I changed for "@Path" but I'm getting the same error. I am testing the link using Postman and it's working fine, retrieving the results that it's supposed to. But in Postman when I add the header I add the Key which is "Authorization" and the value, which is the token. In the app I am not adding the key. Do you think it's because of that? – Marcos Guimaraes Aug 31 '17 at 12:48
  • Yes I think that should be the cause! By the way what is the http code returned by the current call? – João Magalhães Aug 31 '17 at 12:55
  • Can you please tell me how do I get the http response code? – Marcos Guimaraes Aug 31 '17 at 13:36
  • on the `onResponse` method call just log `response.code()` – João Magalhães Aug 31 '17 at 13:39
  • Sorry for the delay, the response code that shows is 401 so it's unauthorized response. Do you know how can I pass this key "Authorization" in the header? – Marcos Guimaraes Aug 31 '17 at 16:40
  • @MarcosGuimaraes yes. You have to add it to the `getProjectCategories` method. The same way you added the "Token" Header. So, if your api reads the authorization key via a header named "Authorization" then your call should be - `@GET("/v3/projects/{projectId}/categories/") Call> getProjectCategories(@Path("projectId") String projectId, @Header("Token") String token, @Header("Authorization") String authorizationKey);` – João Magalhães Aug 31 '17 at 18:20
  • Thanks for the reply. Authorization is not a String, I think it's the name of the field. For an example, it would work like this: requestBuilder.header("Authorization", "Bearer " + token); – Marcos Guimaraes Aug 31 '17 at 18:24
  • Yes that would work. You add it directly to the OkHttpClient as shown on this [link](https://futurestud.io/tutorials/retrofit-add-custom-request-header). Please mark the question as answered if after adding the missing headers it works the way I suggested. – João Magalhães Aug 31 '17 at 18:53
  • Thank you very much! I used the OkHttpClient and I got a neat 200 code! Everything is working now! :) – Marcos Guimaraes Aug 31 '17 at 19:46
0

Add the new fields to the class Category2 with the indication they are transient, i.e, they are not to be serialized. Gson: How to exclude specific fields from Serialization without annotations

public class Category2 {

@SerializedName("_id")
private String _id;
@SerializedName("name")
private String name;
@SerializedName("tasks")
private int tasks;

private transient String createdAt, updatedAt, projectId, companyId;
private transient int __v;

public Category2(String _id, String name, int tasks) {

    this._id = _id;
    this.name = name;
    this.tasks = tasks;
}


public String get_id(){
    return _id;
}
public void set_id(String _id){
    this._id = _id;
}
public String getName(){
    return name;
}
public void setName(String name){
    this.name = name;
}
public int getTasks() {
    return tasks;
}
public void setTasks(int tasks){
    this.tasks = tasks;
}
}

EDIT

Change this

 @GET("/v3/projects/{projectId}/categories/")
Call<List<Category2>> getProjectCategories(@Path("projectId") String projectId, @Header("Authorization") String token);

and this

categoryService =
    CategoryClient.getClient().create(CategoryService.class);

Response<CategoryResponse> response = categoryService.getProjectCategories(projectId,token).execute();

//int statusCode = response.code();
listCategories = new ArrayList<>();
listCategories = response.body().getResults();
System.out.println("Size: " + response.body().getResults().size());
joao86
  • 2,056
  • 1
  • 21
  • 23
  • Thanks for the reply! I did what you suggested but I am getting: Attempt to invoke virtual method 'java.util.List com.construct.v2.models.category.CategoryResponse.getResults()' on a null object reference But I know that for the token and ProjectID I am passing I should not get a null response. – Marcos Guimaraes Aug 31 '17 at 12:36