JSON Question

Retrofit returns data but the pojo object is always empty

I'm trying to consume a service by using retrofit 2. When I call it using a web browser or log the call using an interceptor, there're some results, but my pojo classes are always empty.

The json result content is similar to:

{
"similarartists": {
"artist": [
{
"name": "BarĂ£o Vermelho",
"mbid": "84ac395b-482b-48cb-b381-b9bc420b2dd3",
"match": "1",
"url": "https://www.last.fm/music/Bar%C3%A3o+Vermelho",
"image": [],
"streamable": "0"
},
"@attr": {
"artist": "Cazuza"
}
}
}


And these are my pojo classes:

public class Similarartists {

@SerializedName("artist")
private List<Artist> artist = new ArrayList<>();

@SerializedName("attr")
private Attr attr;

}

public class Artist {

@SerializedName("name")
private String name;

@SerializedName("mbid")
private String mbid;

@SerializedName("match")
private String match;

@SerializedName("url")
private String url;

@SerializedName("image")
private List<Object> image = new ArrayList<>();

@SerializedName("streamable")
private String streamable;
}

public class Attr {
@SerializedName("artist")
private String artist;
}


The service connection class:

public class ApiService {
private static final String BASE_URL = "http://ws.audioscrobbler.com/";
public static final String API_KEY = "XXXXXXXXXX";

private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder()
.readTimeout(30, TimeUnit.SECONDS)
.connectTimeout(30, TimeUnit.SECONDS);

private static Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create());

public static <S> S createService(Class<S> serviceClass) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);

httpClient.addInterceptor(logging);

httpClient.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();

// Request customization: add request headers
Request.Builder requestBuilder = original.newBuilder()
.header("Accept", "application/json")
.header("Content-Type", "application/json")
.method(original.method(), original.body());

Request request = requestBuilder.build();
return chain.proceed(request);
}
});

Retrofit retrofit = builder.client(httpClient.build()).build();

return retrofit.create(serviceClass);
}

}


Ans this is my "button" action:

private void openSearchDialog() {
ArtistService artistService = ApiService.createService(ArtistService.class);
Call<Similarartists> call = artistService.getSimilar("nirvana", ApiService.API_KEY);
call.enqueue(new Callback<Similarartists>() {
@Override
public void onResponse(Call<Similarartists> call, Response<Similarartists> response) {
if (response.isSuccessful()) {
mAdapter.setValues(response.body().getArtist());
Log.i(TAG, "onResponse: " + response.body().toString());
}
}

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


The problem is response.body().getArtist() is always empty. Please help me.

And this's my log result:

-01 22:51:58.844 23843-24043/com.sample.pablo.hellomusic D/OkHttp: {"similarartists":{"artist":[{"name":"Hole","mbid":"1dcc8968-f2cd-441c-beda-6270f70f2863","match":"1","url":"https://www.last.fm/music/Hole","image":[{"#text":"https://lastfm-img2.akamaized.net/i/u/34s/6687f63408074388ae703eb3905e238f.png","size":"small"},{"#text":"https://lastfm-img2.akamaized.net/i/u/64s/6687f63408074388ae703eb3905e238f.png","size":"medium"},{"#text":"https://lastfm-img2.akamaized.net/i/u/174s/6687f63408074388ae703eb3905e238f.png","size":"large"},{"#text":"https://lastfm-img2.akamaized.net/i/u/300x300/6687f63408074388ae703eb3905e238f.png","size":"extralarge"},{"#text":"https://lastfm-img2.akamaized.net/i/u/6687f63408074388ae703eb3905e238f.png","size":"mega"},{"#text":"https://lastfm-img2.akamaized.net/i/u/arQ/6687f63408074388ae703eb3905e238f.png","size":""}],"streamable":"0"}],"@attr":{"artist":"Nirvana"}}}

Answer

Since there is a top-level key of similarartists in the response JSON I think you need another wrapper around this object, like:

public class SimilarArtistsResponse {
  @SerializedName("similarartists")
  private Similarartists similars;
}

And its this object you would specify in your Call<SimilarArtistsResponse>