Подтвердить что ты не робот

Как вызвать десериализацию по умолчанию с помощью gson

Я получаю json, у которого есть поле field.
Если у "поля" есть данные, то есть OBJECT, который содержит много (около 20) других полей, которые также являются объектами. Я могу десериализировать их без проблем.
Но если "поле" не имеет данных, это пустой ARRAY (я знаю, что он сумасшедший, но ответ от сервера и я не могу его изменить). Что-то вроде этого:

Когда пусто:

"extras":[

]

Имеет некоторые данные:

"extras": {
    "22":{ "name":"some name" },
    "59":{ "name":"some other name" },
    and so on...
}

Итак, когда нет данных (пустой массив), я, очевидно, получаю исключение

Caused by: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException:
Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 4319

Я попытался использовать собственный JavaDeserializer:

public class ExtrasAdapter implements JsonDeserializer<Extras> {

@Override
public Extras deserialize(JsonElement json, Type typeOf, 
              JsonDeserializationContext context) throws JsonParseException {
    try {
        JsonObject jsonObject = json.getAsJsonObject();
                    // deserialize normally 

                    // the following does not work, as it makes recursive calls 
                    // to the same function 
        //return context.deserialize(jsonObject, 
                    //                       new TypeToken<Object>(){}.getType());
    } catch (IllegalStateException e) {
        return null;
    }
}

}

Я прочитал json следующим образом

Gson gsonDecoder = new GsonBuilder().registerTypeAdapter(Extras.class, new ExtrasAdapter();
// httpResponse contains json with extras filed. 
Reader reader = new InputStreamReader(httpResponse.getEntity().getContent());
Extras response = gsonDecoder.fromJson(reader, Extras.class);

Я не хочу десериализовать все 20 полей вручную (я знаю, что это вариант), я просто хочу вызвать context.defaultDeserialize() или что-то в этом роде.
Еще раз: у меня нет никаких проблем десериализации нормального json, создания пользовательских объектов, регистрации пользовательских TypeAdapters, пользовательских JavaDeserializers. Все это уже работает. Мне нужно только некоторое решение для обработки данных, которые могут быть как ARRAY, так и OBJECT.
Спасибо за любую помощь.

======================


Ответ Джой работает отлично. Это правильно, что я искал. Я отправлю свой код здесь.

public class SafeTypeAdapterFactory implements TypeAdapterFactory {

public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {

    final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);

    return new TypeAdapter<T>() {

        public void write(JsonWriter out, T value) throws IOException {
            try {
                delegate.write(out, value);
            } catch (IOException e) {
                delegate.write(out, null);
            }
        }

        public T read(JsonReader in) throws IOException {
            try {
                return delegate.read(in);
            } catch (IOException e) {
                Log.w("Adapter Factory", "IOException. Value skipped");
                in.skipValue();
                return null;
            } catch (IllegalStateException e) {
                Log.w("Adapter Factory", "IllegalStateException. Value skipped");
                in.skipValue();
                return null;
            } catch (JsonSyntaxException e) {
                Log.w("Adapter Factory", "JsonSyntaxException. Value skipped");
                in.skipValue();
                return null;
            }

        }
    };
}

}

4b9b3361

Ответ 1

Попробуйте использовать GSON >= 2.2.1 и найдите класс TypeAdapterFactory.

Это даст вам возможность проверять объект перед его десериализацией и применять специальный код, избегая при этом рекурсий.

Вот пример getDelegateAdapter, который вы можете использовать.

Ответ 2

public class ExtrasAdapter implements JsonDeserializer<Extras> {
@Override
public Extras deserialize(JsonElement json, Type typeOf, 
              JsonDeserializationContext context) throws JsonParseException {
    try {
        JsonObject jsonObject = json.getAsJsonObject();
        return new Gson().fromJson(jsonObject , Extras.class); // default deserialization

    } catch (IllegalStateException e) {
        return null;
    }
}

Ответ 3

Для тех, кто приходит в конце, вам не нужно реализовывать TypeAdapter для решения этой проблемы, хотя это вполне приемлемое решение.

Ответ на эту проблему на самом деле находится в исходном вопросе:

public class ExtrasAdapter implements JsonDeserializer<Extras> {

@Override
public Extras deserialize(JsonElement json, Type typeOf, 
          JsonDeserializationContext context) throws JsonParseException {
    try {
        JsonObject jsonObject = json.getAsJsonObject();
        // deserialize normally

        // the following does not work, as it makes recursive calls 
        // to the same function 
        //return context.deserialize(jsonObject, new TypeToken<Object>(){}.getType());
    } catch (IllegalStateException e) {
        return null;
    }
}

Прокомментированный

return context.deserialize(jsonObject, new TypeToken<Object>(){}.getType());

является почти решением. Проблема в два раза. Во-первых, jsonObject - это точный объект, первоначально переданный в эту функцию.

JsonObject jsonObject = json.getAsJsonObject();

Таким образом, передача его в context.deserialize() создаст рекурсию и, в конечном итоге, OOM. Решением здесь является анализ объектов внутри jsonObject.

Это приводит нас к второй проблеме, которая заключается в том, что здесь смешиваются две вещи. "Экстра" - это тип объекта, предположительно с конкретным классом, поддерживающим его (и, возможно, пустым массивом). "Экстра" - это карта. Попытка разобрать "Экстра" как "Экстра" не будет работать. С этой целью я бы предложил следующее определение "Дополнительно":

public class Extras {
    Map<String, Map<String, String>> extras;
    // you could also create a concrete class for "Extra"
    //and have this be a Map<String, Extra>
}

В этом случае проблема становится тривиальной для решения с context.deserialize.

Как я уже говорил выше, TypeAdatper - это совершенно правильное решение для этой проблемы. Я просто считаю, что это больше, чем вам нужно.