0

I have JSON wrapped by "_embedded" tag like below:

{
  "_embedded" : {
    "events" : [ { ... }]}

I'm using retrofit with interface:

public interface IEventRest {
    @GET("/events/search/findByPlaceId")
    Observable<List<Event>> getEventList(@Query("placeId")String placeId);
}

And this is my REST class:

public class EventRest implements IEventRest {
Gson gson = new GsonBuilder()
        .registerTypeAdapter(Event[].class, new MyDeserializer())
        .create();

private Retrofit getRetrofitClient() {
    final HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
    interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

    final OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();

    return new Retrofit.Builder()
            .addConverterFactory(GsonConverterFactory.create(gson))
            .baseUrl(UrlUtils.URL)
            .client(client)
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .build();
}

@Override
public Observable<List<Event>> getEventList(String placeId) {
    final IEventRest placeRest = getRetrofitClient().create(IEventRest.class);

    return placeRest.getEventList(placeId)
            .subscribeOn(Schedulers.io())
            .observeOn(Schedulers.newThread());
}

private class MyDeserializer implements JsonDeserializer<List<Event>> {
    @Override
    public List<Event> deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
            throws JsonParseException {
        JsonElement content = je.getAsJsonObject().get("_embedded");

        Type collectionType = new TypeToken<Collection<Event>>(){}.getType();
        return new Gson().fromJson(content, collectionType);

    }
}
}

But calling placeRest.getEventList(placeId) throws exception:

java.lang.IllegalStateException: Exception thrown on Scheduler.Worker thread. Add `onError` handling.
  at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:57)
  at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:423)
  at java.util.concurrent.FutureTask.run(FutureTask.java:237)
  at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:269)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
  at java.lang.Thread.run(Thread.java:818)
Caused by: rx.exceptions.OnErrorNotImplementedException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
  at rx.internal.util.InternalObservableUtils$ErrorNotImplementedAction.call(InternalObservableUtils.java:386)
  at rx.internal.util.InternalObservableUtils$ErrorNotImplementedAction.call(InternalObservableUtils.java:383)
  at rx.internal.util.ActionSubscriber.onError(ActionSubscriber.java:44)
  at rx.observers.SafeSubscriber._onError(SafeSubscriber.java:152)
  at rx.observers.SafeSubscriber.onError(SafeSubscriber.java:115)
  at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.checkTerminated(OperatorObserveOn.java:276)
  at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.call(OperatorObserveOn.java:219)
  at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
  at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:423) 
  at java.util.concurrent.FutureTask.run(FutureTask.java:237) 
  at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:269) 
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113) 
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) 
  at java.lang.Thread.run(Thread.java:818) 
Caused by: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
  at com.google.gson.stream.JsonReader.beginArray(JsonReader.java:351)
  at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:80)
  at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:61)
  at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:37)
  at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:25)
  at retrofit2.ServiceMethod.toResponse(ServiceMethod.java:117)
  at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:211)
  at retrofit2.OkHttpCall.execute(OkHttpCall.java:174)
  at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$RequestArbiter.request(RxJavaCallAdapterFactory.java:171)
  at rx.internal.operators.OperatorSubscribeOn$1$1$1.request(OperatorSubscribeOn.java:80)
  at rx.Subscriber.setProducer(Subscriber.java:211)
  at rx.internal.operators.OperatorSubscribeOn$1$1.setProducer(OperatorSubscribeOn.java:76)
  at rx.Subscriber.setProducer(Subscriber.java:205)
  at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:152)
  at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:138)
  at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
  at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
  at rx.Observable.unsafeSubscribe(Observable.java:9860)
  at rx.internal.operators.OperatorSubscribeOn$1.call(OperatorSubscribeOn.java:94)
  at rx.internal.schedulers.CachedThreadScheduler$EventLoopWorker$1.call(CachedThreadScheduler.java:221)  

Could you help me to find out what is wrong? Thank you in advance.

2
  • you register for Event[].class, but you ask for List<Event> you need to be consistent. Commented Nov 15, 2016 at 15:00
  • { "_embedded" : { is an Object, not a list of events on it's own. Your data does not start with a [, so you cannot parse it as such. Commented Nov 15, 2016 at 15:03

1 Answer 1

1

I have JSON wrapped by "_embedded" tag

And that is exactly the problem. Gson doesn't know that your data is wrapped, it only cares that at the string that it sees, it is an object.

Pulling the preview, from here... We get a total of three objects

Event.java

(This wasn't generated because you left out what the JSON looks like)

Embedded.java

import java.util.ArrayList;
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

public class Embedded {

    @SerializedName("events")
    @Expose
    private List<Event> events = new ArrayList<Event>();

    public Embedded() {
    }

    public Embedded(List<Event> events) {
        this.events = events;
    }

    public List<Event> getEvents() {
        return events;
    }

    public void setEvents(List<Event> events) {
        this.events = events;
    }

}

Response.java

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

public class Response {

    @SerializedName("_embedded")
    @Expose
    private Embedded embedded;

    public Response() {
    }

    public Response(Embedded embedded) {
        this.embedded = embedded;
    }

    public Embedded getEmbedded() {
        return embedded;
    }


    public void setEmbedded(Embedded embedded) {
        this.embedded = embedded;
    }

}

And, so now, Retrofit cares about a Call<Response>, which you can then call .getEmbedded().getEvents()


Or, you could mess around with MyDeserializer more, since that seems to be the issue with the existing code.

Sign up to request clarification or add additional context in comments.

4 Comments

Ok, it looks it works. Thank you very much! But I don't know if it is a best practise to do that in this way. I cannot find proper solution or good tutorial for deserialization Jsons using GSON and retrofit. Has anyone any experience with that kind a problem?
Retrofit just uses Gson directly, so you can remove that piece from the equation unless you have custom deserializers
I will only add hat geting EventsList Observable looks like: eventRest.getResponse(BuildConfig.PLACE_ID) .map(Response::getEmbedded) .map(Embedded::getEvents) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread());
You could combine those two map statements

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.