java notes Wie behandelt man eine NumberFormatException mit Gson bei der Deserialisierung einer JSON-Antwort?




use gson library in java (6)

Ich lese eine JSON-Antwort mit Gson , die manchmal eine NumberFormatException da ein erwarteter int Wert auf eine leere Zeichenfolge gesetzt ist. Jetzt frage ich mich, wie man mit dieser Art von Ausnahme am besten umgehen kann. Wenn der Wert eine leere Zeichenfolge ist, sollte die Deserialisierung 0 sein.

Erwartete JSON-Antwort:

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

Aber manchmal ist die Laufzeit eine leere Zeichenfolge:

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

Die Java-Klasse sieht folgendermaßen aus:

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

Und die Deserialisierung ist folgende:

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

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

Dabei wird eine com.google.gson.JsonSyntaxException: java.lang.NumberFormatException: empty String da anstelle eines int-Werts ein leerer String zurückgegeben wird.

Gibt es eine Möglichkeit, Gson mitzuteilen, " wenn Sie die Feldlaufzeit des Typs Foo deserialisieren und es eine NumberFormatException gibt, geben Sie einfach den Standardwert 0 zurück "?

Mein Workaround besteht darin, einen String als Typ des runtime anstelle von int . Möglicherweise gibt es jedoch eine bessere Möglichkeit, solche Fehler zu behandeln.


Schnelle und einfache Problemumgehung - Ändern Sie einfach Ihr Elementtypfeld der Laufzeit in String und greifen Sie über einen Getter darauf zu, der die Laufzeit als int zurückgibt:

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

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

=> keine json deserialisierung erforderlich


Zuerst habe ich versucht, einen allgemeinen benutzerdefinierten NumberFormatException für Integer-Werte zu schreiben, um die NumberFormatException und 0 zurückzugeben, aber Gson lässt TypeAdaptors für primitive Typen nicht zu:

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

Danach habe ich eine neue Type FooRuntime für das runtime , so dass die Foo Klasse jetzt so aussieht:

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

Ein Typadapter behandelt den benutzerdefinierten Deserialisierungsprozess:

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

Jetzt muss der GsonBuilder mit GsonBuilder registriert werden, sodass eine leere Zeichenfolge als 0 interpretiert wird, anstatt eine 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);

Für alle, die nach einer Antwort für Kotlin suchen, tun Sie dasselbe wie die beste Antwort, anstatt zu tun

registerTypeAdapter(Long.class.java, adapter)

tun

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


Wie bereits in einem anderen Kommentar erwähnt, können Sie ab GSON 2.3.1 Typadapter für primitive Typen registrieren. Hier handelt es sich um einen Typadapter, der Int- und Integer-Typen behandelt. Standardmäßig wird der Wert 0 (oder Null) für Zeichenfolgen, Boolesche Werte und Nullen verwendet. Dadurch werden weiterhin Zeichenfolgen analysiert, die Zahlen wie "Laufzeit" enthalten: "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);

Sie können es mit dem folgenden Code registrieren

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

Beachten Sie, dass das normale JsonReader.nextInt (), das dies ersetzt, versucht, parseInt und parseDouble für das Token aufzurufen, sodass die interne Logik für die Syntaxanalyse von Ganzzahlen repliziert wird.


Es kann hilfreich sein, im Fall einer NumberFormatException immer einen Standardwert von 0 für die Feldlaufzeit anzunehmen, da dies die einzige Fehlerquelle sein kann.


Hier ist ein Beispiel, das ich für Long geschrieben habe. Dies ist eine bessere Option:

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

Sie können auf diesen Link für mehr verweisen.





gson