Избегайте добавления нулевых объектов POJO в анализируемый массив из ответа на модификацию

1

Мне нужно избегать возврата массива с null элементами после анализа ответа от Web Service, но я не могу понять, как настроить Gson для этого. Я намеревался сделать это таким образом, поскольку мне не нравится обрабатывать проанализированный ответ (необходимость повторять цикл снова).

Позвольте мне показать вам основной пример того, что я имею в виду.

Учитывая это POJO:

public class Item {
  @SerializedName("_id")
  private String id;  // Required field
  private String name;
  @SerializedName("accept_ads")
  private boolean acceptAds;

  // Getters and setters
}

И учитывая ответ JSON от веб-службы:

[
  {"_id": "a01",
  "name": "Test1",
  "accept_ads": true},
  {"name": "Test2"}
]

Я создал этот десериализатор:

public class ItemDeserializer implements JsonDeserializer<Item> {

  @Override
  public Item deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
    Item item = null;

    JsonObject jsonObject = json.getAsJsonObject();
    if (jsonObject.has("_id")) {
      item = new Item();
      item.setId(jsonObject.get("_id").getAsString());
      if (jsonObject.has("name")) {
        item.setName(jsonObject.get("name").getAsString());
      }
      if (jsonObject.has("accept_ads")) {
        item.setAcceptAds(jsonObject.get("accept_ads").getAsBoolean());
      }
    }

    return item;
  }

}

Затем я создаю свои Gson и Retrofit следующим образом:

Gson gson = new GsonBuilder().registerTypeAdapter(Item.class, new ItemDeserializer()).create();

mRetrofit = new Retrofit.Builder().addConverterFactory(GsonConverterFactory.create(gson)).build();

К сожалению, возвращенный ArrayList<Item> содержит и Item и null значение.

UPATE

Благодаря @deluxe1 в комментариях ниже, я прочитал связанную ветку qaru.site/questions/9482821/..., которая сама по себе не то, что я искал, так как она о сериализаторах вместо десерализаторов. Тем не менее, другой ответ в этой теме специально посвящен десериализаторам.

К сожалению, я не могу заставить его работать. Обратите внимание, что в этом случае речь идет о нулевых элементах JsonElements внутри ответа JSON HTTP body, а это не совсем то, чего я пытаюсь достичь. В моем случае элемент JsonElement может быть не нулевым, но в нем отсутствуют обязательные поля, что в итоге делает его недействительным (следовательно, нулевым).

Учитывая приведенные выше примеры классов, я создал следующий десериализатор:

public class NonNullListDeserializer<T> implements JsonDeserializer<List<T>> {

  @Override
  public List<T> deserialize(JsonElement json, Type typeOfT, JsonDeserializationcontext context) throws JsonParseException {

    Gson gson = new GsonBuilder().create();    
    List<T> items = new ArrayList<>();
    for (final JsonElement jsonElement : json.getAsJsonArray() {

      T item = gson.fromJson(jsonElement, typeOfT);
      if (item != null) {
        items.add(item);
      }

    }

    return items;

  }
}

Конечно, я не забыл зарегистрировать его как TypeAdapter в Gson.

Дело в том, что typeOfT - это java.util.ArrayList<com.example.Item> когда этот метод вызывается (но почему??). В результате строка T item = gson.fromJson(jsonElement, typeOfT) не вызывается с внутренним классом (в данном случае Item), вызывая это исключение:

com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at path $

AFAIK, стирание типа делает невозможным следующее:

T item = gson.fromJson(jsonElement, T.class)
Теги:
gson
retrofit

1 ответ

0

NonNullListDeserializer требует небольших модификаций:

class NonNullListDeserializer<T> implements JsonDeserializer<List<T>> {

    @Override
    public List<T> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        if (json instanceof JsonArray) {
            final JsonArray array = (JsonArray) json;
            final int size = array.size();
            if (size == 0) {
                return Collections.emptyList();
            }
            final List<T> list = new ArrayList<>(size);
            for (int i = 0; i < size; i++) {
                // get element type
                Type elementType = $Gson$Types.getCollectionElementType(typeOfT, List.class);
                T value = context.deserialize(array.get(i), elementType);
                if (value != null) {
                    list.add(value);
                }
            }

            return list;
        }

        return Collections.emptyList();
    }
}

Теперь вам нужно десериализовать вашу полезную нагрузку JSON как JSON ниже:

Gson gson = new GsonBuilder()
        .registerTypeAdapter(Item.class, new ItemDeserializer())
        .registerTypeAdapter(List.class, new NonNullListDeserializer<>())
        .setPrettyPrinting()
        .create();

Type itemListType = new TypeToken<List<Item>>() {}.getType();
List<Item> response = gson.fromJson(json, itemListType);

Ещё вопросы

Сообщество Overcoder
Наверх
Меню