2014-11-30 3 views
10

Я использую Retrofit (в сочетании с OkHttp и GSON) для связи с онлайн-сервисом. вебсервиса имеет оболочку по умолчанию вокруг всех этих ответов, похожую на:GSON игнорирует элементы с неправильным типом

{ 
    "resultCode":"OK", 
    "resultObj":"Can be a string or JSON object/array", 
    "error":"", 
    "message":"" 
} 

В этом примере resultCode будет либо OK или NO. Кроме того, error и message имеют только содержимое, если при обработке запроса произошла ошибка. И последнее, но не менее важное: resultObj будет содержать фактический результат вызова (который является строкой в ​​примере, но некоторые вызовы возвращают массив JSON или объект JSON).

Чтобы обработать этот мета-данные, я создал универсальный класс, как этот:

public class ApiResult<T> { 

    private String error; 
    private String message; 
    private String resultCode; 
    private T resultObj; 

    // + some getters, setters etcetera 
} 

Я также создал классы, которые представляют ответы иногда приведенные в resultObj и я определил интерфейс для использования с Модернизированным, что выглядит как это:

public interface SomeWebService { 

    @GET("/method/string") 
    ApiResult<String> someCallThatReturnsAString(); 

    @GET("/method/pojo") 
    ApiResult<SomeMappedResult> someCallThatReturnsAnObject(); 

} 

пока запрос действует это все работает отлично. Но когда ошибка возникает на стороне сервера, она все равно вернет код resultObj со строковым типом. Это приводит someCallThatReturnsAnObject к сбою в библиотеке дооснащения RestAdapter/GSON, с сообщением вроде этого:

retrofit.RetrofitError: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException:

Expected BEGIN_OBJECT but was STRING at line 1 column 110 path $.resultObj

Теперь, наконец, мои вопросы:

  1. Есть ли (простой) способ сказать GSON что он должен просто игнорировать (ака «nullify») свойство, если оно не соответствует ожидаемому типу?
  2. Могу ли я сказать, что GSON обрабатывает пустые строки как null?

ответ

15

Определите модель, как это:

public class ApiResult { 

    private String error; 
    private String message; 
    private String resultCode; 
    private MyResultObject resultObj; 
} 

Затем создайте TypeAdapterFactory для MyResultObject:

public class MyResultObjectAdapterFactory implements TypeAdapterFactory { 

    @Override 
    @SuppressWarnings("unchecked") 
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { 
     if (type.getRawType()!= MyResultObject.class) return null; 

     TypeAdapter<MyResultObject> defaultAdapter = (TypeAdapter<MyResultObject>) gson.getDelegateAdapter(this, type); 
     return (TypeAdapter<T>) new MyResultObjectAdapter(defaultAdapter); 
    } 

    public class MyResultObjectAdapter extends TypeAdapter<MyResultObject> { 

     protected TypeAdapter<MyResultObject> defaultAdapter; 


     public MyResultObjectAdapter(TypeAdapter<MyResultObject> defaultAdapter) { 
      this.defaultAdapter = defaultAdapter; 
     } 

     @Override 
     public void write(JsonWriter out, MyResultObject value) throws IOException { 
      defaultAdapter.write(out, value); 
     } 

     @Override 
     public MyResultObject read(JsonReader in) throws IOException { 
      /* 
      This is the critical part. So if the value is a string, 
      Skip it (no exception) and return null. 
      */ 
      if (in.peek() == JsonToken.STRING) { 
       in.skipValue(); 
       return null; 
      } 
      return defaultAdapter.read(in); 
     } 
    } 
} 

Наконец, Редж Истр MyResultObjectAdapterFactory для Gson:

Gson gson = new GsonBuilder() 
    .registerTypeAdapterFactory(new MyResultObjectAdapterFactory()) 
    .create(); 

Теперь, когда десериализации ApiResult с этим JSON Gson объекта, resultObj будет установлен нулевой если это строка.

Я надеюсь, что это решает проблему =)

+0

Я больше не активно работаю над этим проектом. Если у меня останется свободное время, я попытаюсь подтвердить ваш ответ. –

+0

Работающ awesome, спасибо –

+0

так как вы получаете String из resultObj? –

2

Во-первых, это плохой дизайн API, с которым вы имеете дело. :-(

Вы можете использовать пользовательский номер JsonDeserializer для обработки этого случая.

Зарегистрируйте с Модернизированный:

MyJsonDeserializer deserializer = new MyJsonDeserializer()).create(); 
final Gson gson = new GsonBuilder().registerTypeAdapter(ApiResult.class, deserializer); 
RestAdapter restAdapter = new RestAdapter.Builder() 
    .setEndpoint(API_URL) 
    .setConverter(new GsonConverter(gson)) 
    .build(); 
+0

Вы уверены, что это будет работать с общим классом? Раньше у меня были проблемы с GSON и пользовательскими десериализаторами для родовых классов. PS: Я знаю, что это плохой дизайн, но, к сожалению, я не могу сказать, что происходит с веб-сервисом. –

+0

Не знаю, на самом деле, дженериков. Есть только один способ выяснить. :-) –

4

У меня была аналогичная проблема, и пришли к следующему решению в конце:

вместо того, чтобы пытаться разобрать ваш элемент в String или Array, попробуйте сохранить данные на простом java.lang.Object

Это предотвращает сбои или исключение синтаксического анализа.

например. с GSON Аннотации свойство модели будет выглядеть следующим образом:

@SerializedName("resultObj") 
@Expose 
private java.lang.Object resultObj; 

Далее, когда доступ к данным во время выполнения, вы можете проверить, если ваше resultObj свойства является экземпляром String или нет.

if(apiResultObject instanceof String){ 
    //Cast to string and do stuff 

} else{ 
    //Cast to array and do stuff 

} 

Оригинальное сообщение: https://stackoverflow.com/a/34178082/3708094

0

Я повторно использовать мой réponse POJO.

В одном ответе это String, а другой ответ - List<MajicalModel> magicalField, поэтому один синтаксический анализ не удался.

Я меняю его на com.google.gson.JsonElement magicalField; Это работа для меня. Таким образом, он анализирует raw json и также игнорирует несоответствие типа.

0

вы можете использовать этот код, а

ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); yourObject = objectMapper.readValue(jsonString, .class);

Смежные вопросы