0

I have an app which is querying an endpoint and then simply placing the response in a ListView. However, the 'onFailure' is returning the message 'Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $'.

I have looked at similar questions but cannot seem to figure it out in the context of my app.

In the OnCreate I've setup the base URL for the endpoint then added my API key on and a call is made to an interface which Queries the necessary parameters:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_search);

    myLocationButton = findViewById(R.id.myLocationBtn);

    myLocationButton.setOnClickListener(this);

    eventList = findViewById(R.id.evenListView);
    SearchView searchView = findViewById(R.id.searchView);
    searchView.setIconified(false);
    searchView.clearFocus();


    String API_BASE_URL = "https://app.ticketmaster.com/discovery/v2/";
    final String API_KEY = "HIDDEN";

    OkHttpClient httpClient = new OkHttpClient.Builder()
           .addInterceptor(new Interceptor() {
               @Override
               public okhttp3.Response intercept(Chain chain) throws IOException {
                   Request original = chain.request();
                   HttpUrl originalHttpUrl = original.url();

                   HttpUrl url = originalHttpUrl.newBuilder()
                           .addQueryParameter("apikey", API_KEY)
                           .build();

                   // Request customization: add request headers
                   Request.Builder requestBuilder = original.newBuilder().url(url);

                   Request request = requestBuilder.build();
                   return chain.proceed(request);
               }
           }).build();
    Retrofit.Builder builder =
            new Retrofit.Builder()
                    .baseUrl(API_BASE_URL)
                    .addConverterFactory(
                            GsonConverterFactory.create()
                    );

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

    ITicketMasterAPI event =  retrofit.create(ITicketMasterAPI.class);

    Call<List<TicketMasterEvent>> call = event.getLocalEvents("manchester",
            null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null);

    call.enqueue(new Callback<List<TicketMasterEvent>>() {
        @Override
        public void onResponse(Call<List<TicketMasterEvent>> call, Response<List<TicketMasterEvent>> response) {
            List<TicketMasterEvent> events = response.body();
            eventList.setAdapter(new TMEventAdapter(SearchActivity.this, events));
            Toast.makeText(SearchActivity.this,"something happened", Toast.LENGTH_LONG).show();



        }

        @Override
        public void onFailure(Call<List<TicketMasterEvent>> call, Throwable t) {
            RequestBody test = call.request().body();
            Toast.makeText(SearchActivity.this, "didn't work", Toast.LENGTH_LONG).show();
        }
    });
}

Here's the interface with the queries:

  public interface ITicketMasterAPI {
   @GET("events.json")
   Call  <List<TicketMasterEvent>> getLocalEvents(
           @Query("city") String city,
           @Query("id") String id,
           @Query("keyword") String keyword,
           @Query("attractionId") String attractionId,
           @Query("venueId") String venueId,
           @Query("postalCode") String postalCode,
           @Query("latlong") String latlong,
           @Query("radius") String radius,
           @Query("unit") String unit,
           @Query("source") String source,
           @Query("locale") String locale,
           @Query("marketId") String marketId,
           @Query("startDateTime") String startDateTime,
           @Query("endDateTime") String endDateTime,
           @Query("includeTBA") String includeTBA,
           @Query("includeTBD") String includeTBD,
           @Query("includeTest") String includeTest,
           @Query("size") String size,
           @Query("page") String page,
           @Query("sort") String sort,
           @Query("onsaleStartDateTime") String onsaleStartDateTime,
           @Query("onsaleEndDateTime") String onsaleEndDateTime,
           @Query("countryCode") String countryCode,
           @Query("stateCode") String stateCode,
           @Query("stateCode") String classificationName,
           @Query("classificationId") String classificationId,
           @Query("dmaId") String dmaId,
           @Query("segmentId") String segmentId,
           @Query("segmentName") String segmentName,
           @Query("promoterId") String promoterId,
           @Query("clientVisibility") String clientVisibility,
           @Query("geoPoint") String geoPoint,
           @Query("includeLicensedContent") String includeLicensedContent,
           @Query("includeSpellcheck") String includeSpellcheck);
}

And the model for the data. At the moment I am only wanting the name:

public class TicketMasterEvent {

public String eventName;

public String getEventName() {
    return eventName;
}

UPDATE

I've not changed all the Lists to be of just type 'TicketMasterEvent' which causes it to return a status code of "200 - OK". However, as suggested, I looked at the actual response in Postman and this is what I got:

request response

UPDATE 2

I made the following changes...

public class TicketMasterEvent {

@SerializedName("_embedded")
EventEmbedded embedded;

public String getName() {
    return name;
}

@SerializedName("name")
@Expose
private String name;
@SerializedName("type")
@Expose
private String type;
@SerializedName("id")
@Expose
private int id;
@SerializedName("test")
@Expose
private String test;
@SerializedName("url")
@Expose
private String url;
@SerializedName("locale")
@Expose
private String locale;

@SerializedName("images")
@Expose
private List<EventImages> images;

}

public class EventEmbedded {
    @SerializedName("events")
    @Expose
    List<TicketMasterEvent> events;
}

UPDATE 3 - it works!

Ok, so here's the response I got:

enter image description here

This only seems to work when the call made is of type TicketMasterEvent and not a list - that's the same in the interface too. However, I have noticed in the body that it has some null properties - is that a problem?

And also, by not having it return as a list I can't get it to populate my list view - any ideas on that?

ZachOverflow
  • 387
  • 1
  • 3
  • 14
  • Have you looked at the response that you are getting with Postman or another similar tool? – Rene Ferrari Jan 18 '18 at 20:23
  • @ReneFerrari thanks for the response - i've just posted an update (see above). So it appears the response is an object...I think? But what I'm now trying to figure out is how I can access it within '_embedded' and how it should appear in code, i.e. what type it should be declared as... – ZachOverflow Jan 18 '18 at 20:46
  • rename `eventname` to `name` and add the annotation `@Expose` over it then it should work :) – Rene Ferrari Jan 18 '18 at 20:51
  • Oh sorry well what I personally use to kinda model out the java models with a given JSON response is the [following](http://www.jsonschema2pojo.org/) – Rene Ferrari Jan 18 '18 at 20:57
  • @ReneFerrari thanks for the reply. So I did that in my 'TicketMasterEvent' class but 'name' just comes back as null. Is there something I need to do in order to have the response set that variable or do I need to set the model up to be like the response? At the moment I just want to print the entire thing so I know it's getting it – ZachOverflow Jan 18 '18 at 21:03

1 Answers1

1

Solution 1

So to map your JSON response to a model class you just use this site for help. There just select JSON for Source type and Gson for annotation style. The result should look something like this (pseudocode):

class TicketMasterEvent {
    @Expose
    Embedded embedded;
}

class Embedded {
    @Expose
    List<Event> events;
}

And also the events class. Now it will probably spit out the embedded as _embedded which is not really Java-like. To change that just do the following:

@SerializedName("_embedded")
Embedded embedded;

This annotation tells the Parser to map the _embedded to embedded. Ohyeah and a model class Image also has to be created.

Solution 2

A better but a bit more complex way to do it would be to get the raw JSON response and then iterate over the response Object so you will get access to the JsonArray events which is the only data containing part of the response. How to iterate over a JsonObject is described here. Then you just have to cast the JsonArray to your List<Event>. This can be done like this:

List<Event> events = mGson.fromJson(jsonArray.toString(), new TypeToken<List<Event>>(){}.getType());

Just for your information: your Event class can of course be generated with the site linked at the beginning of Solution 1. To do that just copy the content of the first object in the array :). And in that Event class a List<Image> images also exists to map the images from the JSON response.

Hope this helps!

Rene Ferrari
  • 4,096
  • 3
  • 22
  • 28
  • Thank you so much! I'm going to give the first a go. I was thinking something along those lines from answers I had read elsewhere, but this is a slightly different approach that makes more sense – ZachOverflow Jan 18 '18 at 21:34
  • If you have any more questions just ask :) I will answer them at the latest tomorrow – Rene Ferrari Jan 18 '18 at 21:40
  • Sorry, just another question - how would I represent these new models in my interface? I.e. does the embedded model need to be part of the query? And thanks again, I am really learning a lot about this area now haha – ZachOverflow Jan 18 '18 at 21:42
  • Oh i forgot that.. Leave it as `Call>` as it currently is :) Retrofit in combination with gson should automatically map everything :) Your interface can stay exactly the same. Only the models behind it change! – Rene Ferrari Jan 18 '18 at 21:45
  • 1
    Ok, that's great! I'll probably give this a go a bit later or tomorrow - I'll let you know how I get on :) – ZachOverflow Jan 18 '18 at 21:46
  • Ok :D Hope it will work out for you. If not - like I said - just tell me – Rene Ferrari Jan 18 '18 at 21:47
  • Sorry that I had not answered yet (looked at it thought I will answer you later and then forgot...) . I can take a look at it maybe tomorrow I am not sure (don't have much time currently). Anyway thanks for accepting this answer as most helpful! – Rene Ferrari Feb 06 '18 at 08:45