如何在反序列化JSON响应时处理带有Gson的NumberFormatException

i7uq4tfw  于 2022-11-06  发布在  其他
关注(0)|答案(8)|浏览(174)

我正在阅读一个带有Gson的JSON响应,它有时会返回一个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,“* 如果您反序列化类型为Foo的字段runtime,并且出现NumberFormatException,则只返回默认值0*"?
我的解决方法是使用String作为runtime字段的类型,而不是int,但可能有更好的方法来处理此类错误。

mlmc2os5

mlmc2os51#

下面是我为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);
    }
}

使用Gson util注册适配器:

Gson gson = new GsonBuilder().registerTypeAdapter(Long.class, new LongTypeAdapter()).create();

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

xxhby3vn

xxhby3vn2#

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

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

之后,我为runtime字段引入了一个新的类型FooRuntime,因此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);
bjg7j2ky

bjg7j2ky3#

快速简单的解决方法--只需将运行时的成员类型字段更改为String,并通过getter(以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反序列化

pcww981p

pcww981p4#

我创建了这个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);
    }
}

}

qmelpv7a

qmelpv7a5#

正如在另一个注解中所说的,从GSON 2.3.1开始,你可以为原始类型注册类型适配器,这里有一个类型适配器,它可以处理int和Integer类型,并且可以很好地默认为0(或null),用于字符串、布尔值和null。这将继续解析包含数字的字符串,如“runtime”:“五”。

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,因此这将复制用于解析整数的内部逻辑。

bt1cpqcv

bt1cpqcv6#

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

lo8azlld

lo8azlld7#

此解决方案适用于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();
qlzsbp2j

qlzsbp2j8#

我喜欢无适配器
请原谅我,如果这看起来冒犯,但是,我不得不改变我的Model Class类型所有的String,以克服这个问题
比如我有

data class Info(
    @SerializedName("name") val name : String?,
    @SerializedName("cover") val cover : String?,
    @SerializedName("releaseDate") val releaseDate : Int?,
    @SerializedName("last_modified") val last_modified : Int?,
    @SerializedName("rating") val rating : Int?)

我遇到了NumberFormatException,因此我将其更改为

data class Info(
    @SerializedName("name") val name : String?,
    @SerializedName("cover") val cover : String?,
    @SerializedName("releaseDate") val releaseDate : String?,
    @SerializedName("last_modified") val last_modified : String?,
    @SerializedName("rating") val rating : String?)

现在,我将它们作为

if(!TextUtils.isEmpty(releaseDate){
//go ahead to use it
}

相关问题