2017-01-17 3 views
6

Die API i saugt verwenden müssen, und gibt immer HTTP 200. Aber manchmal ist es die richtige Antwort:Wie mehrere mögliche Antworttypen mit Retrofit2, Gson und Rx behandeln

[{"blah": "blah"}, {"blah": "blah"}] 

und manchmal gibt es Fehler :

{"error": "Something went wrong", "code": 123} 

I Retrofit2 mit Gson Konverter und Rx-Adapter bin mit:

final Api api = new Retrofit.Builder() 
     .baseUrl(URL) 
     .client(client) 
     .addCallAdapterFactory(RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io())) 
     .addConverterFactory(GsonConverterFactory.create()) 
     .build() 
     .create(Api.class); 

und jetzt, wenn ich Fehlerantwort empfange, wird die onError Handler mit folgenden Ausnahme genannt:

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:350) 
    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 retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:152) 
    at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:138) 
    at rx.Observable.unsafeSubscribe(Observable.java:10144) 
    at rx.internal.operators.OperatorSubscribeOn$1.call(OperatorSubscribeOn.java:94) 
    at rx.internal.schedulers.CachedThreadScheduler$EventLoopWorker$1.call(CachedThreadScheduler.java:230) 
    at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55) 
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:428) 
    at java.util.concurrent.FutureTask.run(FutureTask.java:237) 
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:272) 
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607) 
    at java.lang.Thread.run(Thread.java:761) 

Wie kann ich es lösen? Wenn ich die Antwort im Handler onError erhalten könnte, könnte ich es mit der richtigen Fehlermodellklasse analysieren. Aber es scheint, ich kann die rohe Antwort nicht bekommen.

Antwort

2

Sie können einen benutzerdefinierten Gson-Deserializer verwenden, um beide Antworten in einen einzelnen Objekttyp zu mappen. Hier ist eine grobe Skizze der Idee, dass Ihr aktueller Antworttyp List<Map<String, String>> ist, müssen Sie basierend auf Ihrem tatsächlichen Rückgabetyp anpassen. Ich mache auch die Annahme, dass die API immer einen Array auf Erfolg gibt -

public class MyResponse { 
    String error; 
    Integer code; 
    List<Map<String, String>> response; 
} 

interface MyApi { 
    @GET("/") 
    Observable<MyResponse> myCall(); 
} 

private class MyResponseDeserializer implements JsonDeserializer<MyResponse> { 
    public MyResponse deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) 
     throws JsonParseException { 
    MyResponse response = new MyResponse(); 
    if (json.isJsonArray()) { 
     // It is an array, parse the data 
     Type responseType = new TypeToken<List<Map<String, String>>>(){}.getType(); 
     response.response = context.deserialize(json, responseType); 
    } else { 
     // Not an array, parse out the error info 
     JsonObject object = json.getAsJsonObject(); 
     response.code = object.getAsJsonPrimitive("code").getAsInt(); 
     response.error = object.getAsJsonPrimitive("error").getAsString(); 
    } 
    return response; 
    } 
} 

Verwenden Sie die oben einem benutzerdefinierten Gson

Gson gson = new GsonBuilder() 
    .registerTypeAdapter(MyResponse.class, new MyResponseDeserializer()) 
    .create(); 

verwenden, die in Ihren Nachrüst-Builder zu erstellen -

.addConverterFactory(GsonConverterFactory.create(gson)) 

Sie sollten auch Ihre Schnittstelle aktualisieren, um Observable<MyResponse> zurückzukehren. Sie erhalten jetzt sowohl Erfolg als auch Fehler in onNext. Sie müssen das Objekt überprüfen, um festzustellen, ob es sich um eine erfolgreiche Antwort handelt (response != null) oder nicht.

+0

Fehler in Observable erhalten, dass 'java.lang.IllegalArgumentException: Call-Adapter für android.database.Observable kann nicht erstellt werden – SAndroidD

+0

@SAndroidDIt sieht aus, als ob Sie die Datenbank importieren "Observable" anstelle der RX ein. – iagreen

Verwandte Themen