2017-02-12 9 views
1

Я использую модификацию с помощью GSON и RxJava для выполнения сетевых запросов. Я пытаюсь понять, как получить ответ, когда библиотека Gson не сможет его преобразовать.Ручка модификации 2 ошибки преобразования GSON

Это происходит, когда на сервере происходит ошибка, и ответ не соответствует классу, который библиотека Gson пытается преобразовать.

Работа должна заключаться в создании перехватчика и кэшировании ответа, прежде чем мы попытаемся его преобразовать. Но это просто плохое программирование, так как проблема станет неуправляемой, как только мы начнем выполнять параллельные запросы.

сервис определяется следующим образом. Класс ответа содержит только код состояния и общий тип, называемый данными.

Retrofit getService() { 
    return new Retrofit.Builder() 
      .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 
      .addConverterFactory(GsonConverterFactory.create()) 
      .baseUrl(url) 
      .client(clientBuilder.build()) 
      .build(); 
} 
public Observable<Response<String>> userLogin(String username, String password) { 

    return getService().create(Account.class) 
      .login(username, password) 
      .subscribeOn(Schedulers.newThread()) 
      .observeOn(AndroidSchedulers.mainThread()); 
} 

Где-то в коде мы создаем запрос

getService().userLogin(email, password) 
     .subscribeOn(Schedulers.newThread()) 
     .observeOn(AndroidSchedulers.mainThread()) 
     .subscribe(onSuccess(), onError()); 

protected Action1<Response<String>> onSuccess(){ 

    return new Action1<Response<String>>() { 
     @Override 
     public void call(Response<String> response) { 
      // Process the response 
     } 
    }; 
} 
protected Action1<Throwable> onError(){ 
    return new Action1<Throwable>() { 
     @Override 
     public void call(Throwable throwable) { 

      if (throwable instanceof HttpException) { 
       ResponseBody body = ((HttpException) throwable).response().errorBody(); 
       // Handle the error 
      } 
     } 
    }; 

Проблема возникает, когда сервер возвращает нечто иное, чем строка. Например, объект или массив. Здесь GsonConverterFactory выдает ошибку, которая будет обнаружена методом onError. Мне интересно, как я могу получить ответ.

Возвращаемое значение, которое возвращается, имеет тип JsonSyntaxException и, к сожалению, оно не содержит оригинальное тело ответа, которое библиотека GSON пыталась преобразовать.

ответ

0

Вчера я предложил similar solution аналогичному вопросу, однако вам, похоже, нужна небольшая модификация ответа. Ответ также содержит некоторые комментарии по вопросам эффективности и потребления памяти. Если вы хотите использовать это решение, вы можете создать специальный обработчик отказа, который может создать специальное исключение держит некоторую информацию отказа:

final class BadPayloadExceptionConversionThrowableConsumer 
     implements IConversionThrowableConsumer { 

    private static final int MAX_STREAM_BUFFER_LENGTH = 8 * 1024; 

    private static final IConversionThrowableConsumer badPayloadExceptionConversionThrowableConsumer = new BadPayloadExceptionConversionThrowableConsumer(); 

    private BadPayloadExceptionConversionThrowableConsumer() { 
    } 

    static IConversionThrowableConsumer getBadPayloadExceptionConversionThrowableConsumer() { 
     return badPayloadExceptionConversionThrowableConsumer; 
    } 

    @Override 
    public void accept(final MediaType contentType, final long contentLength, final InputStream inputStream, final Throwable ex) 
      throws IOException { 
     final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(MAX_STREAM_BUFFER_LENGTH); 
     copy(limit(inputStream, MAX_STREAM_BUFFER_LENGTH), byteArrayOutputStream); 
     final ByteArrayInputStream bufferedInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); 
     throw new BadPayloadException(ex, contentType, contentLength, bufferedInputStream); 
    } 

    static final class BadPayloadException 
      extends IOException { 

     private final MediaType contentType; 
     private final long contentLength; 
     private final InputStream inputStream; 

     private BadPayloadException(final Throwable cause, final MediaType contentType, final long contentLength, final InputStream inputStream) { 
      super(null, cause); 
      this.contentType = contentType; 
      this.contentLength = contentLength; 
      this.inputStream = inputStream; 
     } 

     MediaType getContentType() { 
      return contentType; 
     } 

     long getContentLength() { 
      return contentLength; 
     } 

     InputStream getInputStream() { 
      return inputStream; 
     } 

    } 

} 

Вместо входа он бросает только специальное исключение частного конструктора, который может быть instanceof-й издом на сайте вызова. Входной поток должен быть буферизирован до некоторого предела по причинам, которые также прокомментированы в соответствующем вопросе (особенно почему делегировано InputStream, но не ResponseBody). Теперь, как он может быть использован:

service.getFooBar() 
     .subscribe(
       out::println, 
       t -> { 
        if (t instanceof BadPayloadException) { 
         try { 
          final BadPayloadException badPayloadException = (BadPayloadException) t; 
          err.println("Content type = " + badPayloadException.getContentType()); 
          err.println("Content length = " + badPayloadException.getContentLength()); 
          err.print("Content  = "); 
          copy(badPayloadException.getInputStream(), err); 
         } catch (final IOException ex) { 
          throw new RuntimeException(ex); 
         } 
        } else { 
         err.println(t.getClass()); 
        } 
       } 
     ); 

Обратите внимание, что copy и limit методы являются статическими импорт из Google гуавы ByteStreams. out и err - статический импорт для System.out и System.err соответственно. Выход

Пример:

Content type = application/json 
Content length = -1 
Content  = {#"foo":1,"bar":2}