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

Sou*_*ink 45 java json gson deserialization

我正在使用Gson读取JSON响应,它返回somtimes a NumberFormatException因为预期int值设置为空字符串.现在我想知道处理这种异常的最佳方法是什么.如果值为空字符串,则反序列化应为0.

预期的JSON响应:

{
   "name" : "Test1",
   "runtime" : 90
}
Run Code Online (Sandbox Code Playgroud)

但有时运行时是一个空字符串:

{
   "name" : "Test2",
   "runtime" : ""
}
Run Code Online (Sandbox Code Playgroud)

java类看起来像这样:

public class Foo
{
    private String name;
    private int runtime;
}
Run Code Online (Sandbox Code Playgroud)

反序列化是这样的:

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

Gson gson = new Gson();
Foo foo = gson.fromJson(input, Foo.class);
Run Code Online (Sandbox Code Playgroud)

抛出一个com.google.gson.JsonSyntaxException: java.lang.NumberFormatException: empty String因为返回一个空String而不是一个int值.

有没有办法告诉Gson," 如果你反序列化runtimeType 的字段Foo并且有一个NumberFormatException,只返回默认值0 "?

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

Man*_*wal 33

这是我为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);
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以参考此链接了解更多信息.

  • Gson gson = new GsonBuilder().registerTypeAdapter(Long.class,new LongTypeAdapter()).create(); (3认同)
  • 对于任何正在为 kotlin 寻找答案的人,请执行与最佳答案相同的操作,但不要执行 `registerTypeAdapter(Long.class.java, adapter)` 而是执行 `registerTypeAdapter(java.lang.Long::class.java,适配器)` (3认同)
  • 不工作。我仍然收到同样的错误 java.lang.NumberFormatException: 预期很长,但为 16.6 (2认同)

Sou*_*ink 14

首先,我尝试为Integer值编写一个通用的自定义类型适配器,以捕获NumberFormatException并返回0,但Gson不允许TypeAdaptors用于原始类型:

java.lang.IllegalArgumentException: Cannot register type adapters for class java.lang.Integer
Run Code Online (Sandbox Code Playgroud)

之后我FooRuntime为该runtime字段引入了一个新的Type ,所以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;
    }
}
Run Code Online (Sandbox Code Playgroud)

类型适配器处理自定义反序列化过程:

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());
    }
}
Run Code Online (Sandbox Code Playgroud)

现在有必要使用GsonBuilder注册类型适配器,因此空字符串被解释为0而不是抛出a 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);
Run Code Online (Sandbox Code Playgroud)

  • 哇,这是一些应该是微不足道的代码:-s (22认同)
  • 从版本2.3.1开始,GSON现在允许您为基本类型注册类型适配器.我能够通过为`Integer.class`注册`JsonDeserializer`来解决同样的问题,在`NumberFormatException`的情况下返回0. (3认同)

luk*_*kle 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

=>没有json反序列化必要

  • 这绝对是作弊兄弟 (2认同)

小智 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

}


sea*_*her 5

正如另一条评论中所述,从 GSON 2.3.1 开始,您可以为原始类型注册类型适配器,这是一个处理 int 和 Integer 类型的类型适配器,并且对于字符串、布尔值和 null 优雅地默认为 0(或 null)。这将继续解析其中包含数字的字符串,例如“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);
Run Code Online (Sandbox Code Playgroud)

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

Gson gson = new GsonBuilder()
            .registerTypeAdapterFactory(UNRELIABLE_INTEGER_FACTORY)
            .create();
Run Code Online (Sandbox Code Playgroud)

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