usar tutorial libreria leer example deserializar con como java json deserialization gson

java - tutorial - Cómo manejar una NumberFormatException con Gson en la deserialización de una respuesta JSON



leer json en java (6)

Al principio, intenté escribir un adaptador de tipo personalizado general para los valores de enteros, para capturar la NumberFormatException y devolver 0, pero Gson no permite los adaptadores de tipo para los tipos primitivos:

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

Después de eso introduje un nuevo Tipo FooRuntime para el campo de runtime , por lo que la clase Foo ahora se ve así:

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

Un adaptador de tipo maneja el proceso de deserialización personalizado:

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

Ahora es necesario usar GsonBuilder para registrar el adaptador de tipo, por lo que una cadena vacía se interpreta como 0 en lugar de lanzar una 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);

Estoy leyendo una respuesta JSON con Gson , que a NumberFormatException devuelve una NumberFormatException porque un valor int esperado se establece en una cadena vacía. Ahora me pregunto cuál es la mejor manera de manejar este tipo de excepción. Si el valor es una cadena vacía, la deserialización debe ser 0.

Respuesta esperada de JSON:

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

Pero a veces el tiempo de ejecución es una cadena vacía:

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

La clase de java se ve así:

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

Y la deserialización es esta:

String input = "{/n" + " /"name/" : /"Test/",/n" + " /"runtime/" : /"/"/n" + "}"; Gson gson = new Gson(); Foo foo = gson.fromJson(input, Foo.class);

Que lanza una com.google.gson.JsonSyntaxException: java.lang.NumberFormatException: empty String porque se devuelve una String vacía en lugar de un valor int.

¿Hay alguna manera de decirle a Gson que " si deserializa el runtime de runtime de campo del Tipo Foo y hay una excepción NumberFormatException, simplemente devuelve el valor predeterminado 0 "?

Mi solución es usar una String como el Tipo de campo del runtime de runtime lugar de int , pero quizás haya una mejor manera de manejar tales errores.


Aquí está el ejemplo que hice para el tipo largo. Esta es la mejor opción:

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

Puede consultar este enlace para más.


Como se indicó en otro comentario, a partir de GSON 2.3.1 puede registrar adaptadores de tipo para los tipos primitivos, aquí hay un adaptador de tipo que maneja los tipos int e Integer, y por defecto se ajusta a 0 (o nulo) para cadenas, booleanos y nulos. Esto continuará analizando cadenas que tienen números como "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);

Puedes registrarlo con el siguiente código

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

Tenga en cuenta que el JsonReader.nextInt () normal que reemplaza los intentos de llamar a parseInt y parseDouble en el token, por lo tanto, replicará la lógica interna para analizar enteros.


He hecho este TypeAdapter que comprueba si hay cadenas vacías y devuelve 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); } }

}


Podría ayudarlo a asumir siempre un valor predeterminado de 0 para el runtime de runtime del campo en caso de una excepción NumberFormatException, ya que puede ser la única fuente de error.


Solución rápida y fácil: simplemente cambie el campo de tiempo de ejecución de tipo miembro a Cadena y acceda a él a través de getter que devuelve el tiempo de ejecución como un int:

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

=> no es necesaria la deserialización de json