web-dev-qa-db-ja.com

JSON応答の逆シリアル化でGsonでNumberFormatExceptionを処理する方法

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

Int値の代わりに空の文字列が返されるため、com.google.gson.JsonSyntaxException: Java.lang.NumberFormatException: empty Stringがスローされます。

Gsonに、「Type runtimeのフィールドFooをデシリアライズし、NumberFormatExceptionがある場合、デフォルト値0を返すようにする方法はありますか? "?

私の回避策は、Stringの代わりにruntimeフィールドのタイプとしてintを使用することですが、そのようなエラーを処理するより良い方法があるかもしれません。

43
Soundlink

最初に、NumberFormatExceptionをキャッチして0を返すために、整数値用の一般的なカスタムタイプアダプターを記述しようとしましたが、Gsonはプリミティブ型のTypeAdaptorsを許可していません。

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

その後、FooRuntimeフィールドに新しいType 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を使用する必要があるため、空の文字列はNumberFormatExceptionをスローする代わりに0として解釈されます。

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);
14
Soundlink

これは私が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);
    }
}

詳しくは このリンク を参照してください。

30

すばやく簡単な回避策-ランタイムのメンバータイプフィールドをStringに変更し、ランタイムを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のシリアル化解除は不要

10
lukle

空の文字列をチェックして0を返すこのTypeAdapterを作成しました

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

}

6
Valdo Raya

NumberFormatExceptionの場合、エラーの唯一の原因になる可能性があるため、フィールドruntimeに対して常に0のデフォルト値を想定すると役立つ場合があります。

2
Milad Naseri

Kotlinについてこれに関する回答を探している人は、トップの回答と同じことをしてください。

registerTypeAdapter(Long.class.Java, adapter)

行う

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

1
lucas_sales