如何在反序列化JSON响应时使用Gson处理NumberFormatException

时间:2012-01-14 16:09:46

标签: java json deserialization gson

我正在使用Gson读取JSON响应,该响应返回somtimes 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因为返回空String而不是int值。

有没有办法告诉Gson,“如果您反序列化类型runtime的字段Foo并且存在NumberFormatException,则只返回默认值0 ” ?

我的解决方法是使用String作为runtime字段的类型而不是int,但也许有更好的方法来处理此类错误。

8 个答案:

答案 0 :(得分:30)

这是我为Long类型做的例子。这是更好的选择:

    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);
    }
}

您可以参考this link了解更多信息。

答案 1 :(得分:14)

首先,我尝试为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 :(得分:10)

快速简便的解决方法 - 只需将运行时的成员类型字段更改为String,然后通过将运行时返回为int的getter访问它:

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

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

=&GT;没有json反序列化需要

答案 3 :(得分:6)

我已经制作了这个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);
    }
}

}

答案 4 :(得分:2)

在NumberFormatException的情况下,对于字段0,始终假设默认值为runtime可能会有所帮助,因为它可能是唯一的错误来源。

答案 5 :(得分:1)

如另一条评论所述,从GSON 2.3.1开始,您可以注册基本类型的类型适配器,这是一个处理int和Integer类型的类型适配器,并且对于字符串,布尔值和空值。这将继续解析其中包含数字的字符串,例如“ runtime”:“ 5”。

public static final TypeAdapter<Number> UNRELIABLE_INTEGER = new TypeAdapter<Number>() {
    @Override
    public Number read(JsonReader in) throws IOException {
        JsonToken jsonToken = in.peek();
        switch (jsonToken) {
            case NUMBER:
            case STRING:
                String s = in.nextString();
                try {
                    return Integer.parseInt(s);
                } catch (NumberFormatException ignored) {
                }
                try {
                    return (int)Double.parseDouble(s);
                } catch (NumberFormatException ignored) {
                }
                return null;
            case NULL:
                in.nextNull();
                return null;
            case BOOLEAN:
                in.nextBoolean();
                return null;
            default:
                throw new JsonSyntaxException("Expecting number, got: " + jsonToken);
        }
    }
    @Override
    public void write(JsonWriter out, Number value) throws IOException {
        out.value(value);
    }
};
public static final TypeAdapterFactory UNRELIABLE_INTEGER_FACTORY = TypeAdapters.newFactory(int.class, Integer.class, UNRELIABLE_INTEGER);

您可以使用以下代码进行注册

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

请注意,此示例替换的普通JsonReader.nextInt()尝试在令牌上调用parseInt和parseDouble,因此这将复制用于解析整数的内部逻辑。

答案 6 :(得分:0)

对于正在为kotlin寻找答案的人,请与最高答案相同,但不要这样做

registerTypeAdapter(Long.class.java, adapter)

registerTypeAdapter(java.lang.Long::class.java, adapter)

答案 7 :(得分:0)

此解决方案适用于Double类型。这仅适用于非基本类型:

public class DoubleGsonTypeAdapter implements JsonDeserializer<Double> {

    @Override
    public Double deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
        Double result = null;
        try {
            result = jsonElement.getAsDouble();
        } catch (NumberFormatException e) {
            return result;
        }
        return result;
    }
}

型号:

@SerializedName("rateOfInterest")
public Double rateOfInterest;
@SerializedName("repaymentTenure")
public Double repaymentTenure;
@SerializedName("emiAmount")
public Double emiAmount;

改装客户端:

Gson gson = new GsonBuilder().registerTypeAdapter(Double.class, new DoubleGsonTypeAdapter()) .create();

Retrofit retrofit = new Retrofit.Builder()
                .client(okHttpClient)
                .baseUrl(API_BASE_URL)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .build();