Мне нужно избегать возврата массива с 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)
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);