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

Как обрабатывать NumberFormatException с Gson при десериализации JSON-ответ

Я читаю ответ JSON с Gson, который возвращает somtimes a NumberFormatException, потому что установлено ожидаемое значение int к пустой строке. Теперь мне интересно, какой лучший способ справиться с этим видом исключения. Если значение представляет собой пустую строку, десериализация должна быть 0.

Ожидаемый ответ JSON:

{
   "name" : "Test1",
   "runtime" : 90
}

Но иногда среда выполнения представляет собой пустую строку:

{
   "name" : "Test2",
   "runtime" : ""
}

Класс java выглядит следующим образом:

public class Foo
{
    private String name;
    private int runtime;
}

И десериализация такова:

String input = "{\n" +
               "   \"name\" : \"Test\",\n" +
               "   \"runtime\" : \"\"\n" +
               "}";

Gson gson = new Gson();
Foo foo = gson.fromJson(input, Foo.class);

Что выбрал com.google.gson.JsonSyntaxException: java.lang.NumberFormatException: empty String, потому что вместо значения int возвращается пустой String.

Есть ли способ рассказать Gson: "Если вы десериализуете поле runtime для типа Foo и существует исключение NumberFormatException, просто верните значение по умолчанию 0"?

Моим обходным решением является использование String в качестве типа поля runtime вместо int, но, возможно, есть лучший способ справиться с такими ошибками.

4b9b3361

Ответ 1

Сначала я попытался написать общий адаптер настраиваемого типа для значений Integer, чтобы поймать NumberFormatException и вернуть 0, но Gson не разрешает TypeAdaptors для примитивных типов:

java.lang.IllegalArgumentException: Cannot register type adapters for class java.lang.Integer

После этого я ввел новый тип FooRuntime для поля runtime, поэтому класс Foo теперь выглядит следующим образом:

public class Foo
{
    private String name;
    private FooRuntime runtime;

    public int getRuntime()
    {
        return runtime.getValue();
    }
}

public class FooRuntime
{
    private int value;

    public FooRuntime(int runtime)
    {
        this.value = runtime;
    }

    public int getValue()
    {
        return value;
    }
}

Адаптер типа обрабатывает процесс десериализации:

public class FooRuntimeTypeAdapter implements JsonDeserializer<FooRuntime>, JsonSerializer<FooRuntime>
{
    public FooRuntime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
    {
        int runtime;
        try
        {
            runtime = json.getAsInt();
        }
        catch (NumberFormatException e)
        {
            runtime = 0;
        }
        return new FooRuntime(runtime);
    }

    public JsonElement serialize(FooRuntime src, Type typeOfSrc, JsonSerializationContext context)
    {
        return new JsonPrimitive(src.getValue());
    }
}

Теперь необходимо использовать GsonBuilder для регистрации адаптера типа, поэтому пустая строка интерпретируется как 0 вместо того, чтобы бросать NumberFormatException.

String input = "{\n" +
               "   \"name\" : \"Test\",\n" +
               "   \"runtime\" : \"\"\n" +
               "}";

GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(FooRuntime.class, new FooRuntimeTypeAdapter());
Gson gson = builder.create();
Foo foo = gson.fromJson(input, Foo.class);

Ответ 2

Вот пример, который я сделал для длинного типа. Это лучший вариант:

    public class LongTypeAdapter extends TypeAdapter<Long>{
    @Override
    public Long read(JsonReader reader) throws IOException {
        if(reader.peek() == JsonToken.NULL){
            reader.nextNull();
            return null;
        }
        String stringValue = reader.nextString();
        try{
            Long value = Long.valueOf(stringValue);
            return value;
        }catch(NumberFormatException e){
            return null;
        }
    }
    @Override
    public void write(JsonWriter writer, Long value) throws IOException {
        if (value == null) {
            writer.nullValue();
            return;
        }
        writer.value(value);
    }
}

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

Ответ 3

Быстрое и простое обходное решение. Просто измените поле типа члена среды выполнения на String и получите доступ к нему через getter, который возвращает runtime как int:

public class Foo
{
    private String name;
    private String runtime;

    public int getRuntime(){
        if(runtime == null || runtime.equals("")){
            return 0;
        }
        return Integer.valueOf(trackId);
    }
}

= > нет необходимости десериализации json

Ответ 4

Я создал этот TypeAdapter, который проверяет наличие пустых строк и возвращает 0

public class IntegerTypeAdapter extends TypeAdapter<Number> {
@Override
public void write(JsonWriter jsonWriter, Number number) throws IOException {
    if (number == null) {
        jsonWriter.nullValue();
        return;
    }
    jsonWriter.value(number);
}

@Override
public Number read(JsonReader jsonReader) throws IOException {
    if (jsonReader.peek() == JsonToken.NULL) {
        jsonReader.nextNull();
        return null;
    }

    try {
        String value = jsonReader.nextString();
        if ("".equals(value)) {
            return 0;
        }
        return Integer.parseInt(value);
    } catch (NumberFormatException e) {
        throw new JsonSyntaxException(e);
    }
}

}

Ответ 5

Это может помочь вам всегда принимать значение по умолчанию 0 для поля runtime в случае исключения NumberFormatException, поскольку оно может быть единственным источником ошибок.