Reputation: 413
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.
Upvotes: 0
Views: 781
Reputation: 191711
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.
Upvotes: 1