8

I have used gson before to automatically convert to pojo's.

but now im trying to use retrofit to convert an api result to objects.

As long as the json has named arrays of objects this is no problem:

e.g.:

{
    items:[
        {"name":"foo"},
        {"name":"bar"}
    ]
}

public class AnItem {
    String name;
}

public class MyItems {
    List<AnItem> items;
}

public interface MyApi {
    @GET("/itemlist")
    Call<MyItems> loadItems();
}

public boolean onOptionsItemSelected(MenuItem item) {
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("https://someurl.com/api")
            .addConverterFactory(GsonConverterFactory.create())
            .build();
    MyApi myApi = retrofit.create(MyApi.class);
    Call<MyApi> call = myApi.loadItems();
    call.enqueue(this);
    return true;
}

@Override
public void onResponse(Response<MyItems> response, Retrofit retrofit) {
    ArrayAdapter<AnItem> adapter = (ArrayAdapter<AnItem>) getListAdapter();
    adapter.clear();
    adapter.addAll(response.body().items);
}

this will automatically work.

but how to do this if the json array isn't named?

e.g.:

[
    {"name":"foo"},
    {"name":"bar"}
]

i know how to do it when not using retrofit with parsejson and iterate over array to get my objects but i cant do it like that because that part is automated with retrofit and gson.

ive been really stuck on this problem for some time now and any pointers would be really appreciated..

UPDATE:

below are the parts i have changed MyItems to List<AnItem>:



public class AllTemplatesRetroActivity extends ListActivity implements Callback<List<AnItem>> {

protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
    requestWindowFeature(Window.FEATURE_PROGRESS);
    ArrayAdapter<AnItem> arrayAdapter =
            new ArrayAdapter<AnItem>(this,
                    android.R.layout.simple_list_item_1,
                    android.R.id.text1,
                    new ArrayList<AnItem>());
    setListAdapter(arrayAdapter);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://someurl.com/api")
        .addConverterFactory(GsonConverterFactory.create())
        .build();

    MyApi myApi = retrofit.create(MyApi.class);

    Call<List<AnItem>> call = myApi.loadItems();
    call.enqueue(this);
    return true;
}

@Override
public void onResponse(Response<List<AnItem>> response, Retrofit retrofit) {
    ArrayAdapter<AnItem> adapter = (ArrayAdapter<AnItem>) getListAdapter();
    adapter.clear();
    Log.i("##MYLOG###", "## response:"+ response.message());
    adapter.addAll(response.body());
}
dilux
  • 277
  • 1
  • 3
  • 10
  • You can handle the last json by changing Call to Call> – Emma Jan 05 '16 at 23:36
  • ok, i changed it but one problem remains, what to make of: adapter.addAll(response.body().items); if i change that to : adapter.addAll(response.body()); i get "java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.Object[] java.util.Collection.toArray()' on a null object reference" – dilux Jan 06 '16 at 22:36
  • You might not get a correct json conversion back yet. Could you update your change in question and check what you have in response.body() ? – Emma Jan 06 '16 at 23:45
  • actually my updated code goes to the onFailure block and has message:"Expected BEGIN_ARRAY but was STRING at line 1 column 1 path $" no idea what that even means. – dilux Jan 07 '16 at 21:30
  • That means your response json is not in format of [ {"name":"foo"}, {"name":"bar"}]. Try changing Response> to Response without a type for now to see what json you are getting. If you want to try logging, try this http://stackoverflow.com/a/33256827/2956135 I think you can see the more detailed response. – Emma Jan 07 '16 at 21:59
  • im using a much older version.. but ill look into it. strange thing is if i do a curl request to that same end point in my terminal i DO get that particular piece of json back so that is not the problem. what do you exactly mean by: " Try changing Response> to Response without a type"? – dilux Jan 07 '16 at 22:11
  • This is retrofit 2, is it ? – Emma Jan 07 '16 at 22:16
  • retrofit:2.0.0-beta2 – dilux Jan 07 '16 at 22:20
  • it works! somehow this fails: .baseUrl("https://someurl.com/api") @GET("/itemlist") but this works: .baseUrl("https://someurl.com") @GET("/api/itemlist") also that HttpLoggingInterceptor thing works very well. thanks a lot for your patience! – dilux Jan 07 '16 at 22:32
  • Try .baseUrl("https://someurl.com/api/") and @GET("itemlist"). Add the '/' in the last char of baseUrl and remove '/' from @GET. If this works, I will write an explanation in answer. the stackoverflow I posted works for Retrofit2 :) – Emma Jan 07 '16 at 22:32
  • got it working, see my previous comment. how can i accept your answer? – dilux Jan 07 '16 at 22:35
  • can you also include your first comment in your answer (about changing object to List to get an array instead of object as my question was initially about that) – dilux Jan 07 '16 at 22:41
  • Sure. Hope it is clear enough. – Emma Jan 07 '16 at 22:47

1 Answers1

10

To Handle json response like this:

[
    {"name":"foo"},
    {"name":"bar"}
]

Use Call< List < AnItem>>


In Retrofit2, HttpUrl.resolve() is used to resolve baseUrl and annotated path.

Due to how this works,

This works:

.baseUrl("http://someurl.com")

@GET("/api/itemlist")    --> resolved to http://someurl.com/api/itemlist

or this works

.baseUrl("http://someurl.com/api/")

@GET("itemlist")    --->  resolved to http://someurl.com/api/itemlist

However, this would not work

.baseUrl("http://someurl.com/api")

 @GET("/itemlist")   ---> resolved to http://someurl.com/itemlist
Emma
  • 8,518
  • 1
  • 18
  • 35