How to handle a NumberFormatException with Gson in deserialization a JSON response

Tags: , , ,



I’m reading a JSON response with Gson, which returns somtimes a NumberFormatException because an expected int value is set to an empty string. Now I’m wondering what’s the best way to handle this kind of exception. If the value is an empty string, the deserialization should be 0.

Expected JSON response:

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

But sometimes the runtime is an empty string:

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

The java class looks like this:

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

And the deserialization is this:

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

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

Which throws a com.google.gson.JsonSyntaxException: java.lang.NumberFormatException: empty String because an empty String is returned instead of an int value.

Is there a way to tell Gson, “if you deserialize the field runtime of the Type Foo and there is a NumberFormatException, just return the default value 0“?

My workaround is to use a String as the Type of the runtime field instead of int, but maybe there is a better way to handle such errors.

Answer

At first, I tried to write a general custom type adaptor for Integer values, to catch the NumberFormatException and return 0, but Gson doesn’t allow TypeAdaptors for primitive Types:

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

After that I introduced a new Type FooRuntime for the runtime field, so the Foo class now looks like this:

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

A type adaptor handles the custom deserialization process:

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

Now it’s necessary to use GsonBuilder to register the type adapter, so an empty string is interpreted as 0 instead of throwing 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);


Source: stackoverflow