¿Por qué Gson fromJson lanza una JsonSyntaxException: esperaba algún tipo pero era de otro tipo?

(Esta publicación debe ser una pregunta canónica con una respuesta de muestra que se proporciona a continuación).


Estoy tratando de deserializar algunos contenidos JSON en un tipo POJO personalizado con Gson#fromJson(String, Class) .

Esta pieza de código

 import com.google.gson.Gson; public class Sample { public static void main(String[] args) { String json = "{\"nestedPojo\":[{\"name\":null, \"value\":42}]}"; Gson gson = new Gson(); gson.fromJson(json, Pojo.class); } } class Pojo { NestedPojo nestedPojo; } class NestedPojo { String name; int value; } 

arroja la siguiente excepción

 Exception in thread "main" com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 16 path $.nestedPojo at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:200) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:103) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:196) at com.google.gson.Gson.fromJson(Gson.java:810) at com.google.gson.Gson.fromJson(Gson.java:775) at com.google.gson.Gson.fromJson(Gson.java:724) at com.google.gson.Gson.fromJson(Gson.java:696) at com.example.Sample.main(Sample.java:23) Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 16 path $.nestedPojo at com.google.gson.stream.JsonReader.beginObject(JsonReader.java:387) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:189) ... 7 more 

¿Por qué Gson no puede convertir correctamente mi texto JSON a mi tipo POJO?

Como el mensaje de excepción dice

 Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 16 path $.nestedPojo 

mientras deserializaba, Gson esperaba un objeto JSON, pero encontró una matriz JSON. Como no podía convertirse de uno a otro, arrojó esta excepción.

El formato JSON se describe aquí . En resumen, define los siguientes tipos: objetos, matrices, cadenas, números, null y los valores booleanos true y false .

En Gson (y en la mayoría de los analizadores JSON), existen las siguientes asignaciones: una cadena JSON se correlaciona con una String Java; un número JSON se asigna a un tipo de Number Java; una matriz JSON se asigna a un tipo de Collection o un tipo de matriz; un objeto JSON se asigna a un tipo de Map Java o, típicamente, un tipo POJO personalizado (no mencionado anteriormente); null asigna a null de Java, y los valores booleanos se correlacionan con true y false Java.

Gson repite el contenido JSON que usted proporciona e intenta deserializarlo con el tipo correspondiente que ha solicitado. Si el contenido no coincide o no se puede convertir al tipo esperado, arrojará una excepción correspondiente.

En su caso, proporcionó el siguiente JSON

 { "nestedPojo": [ { "name": null, "value": 42 } ] } 

En la raíz, este es un objeto JSON que contiene un miembro llamado nestedPojo que es una matriz JSON. Esa matriz JSON contiene un único elemento, otro objeto JSON con dos miembros. Teniendo en cuenta las asignaciones definidas anteriormente, esperaría que este JSON se nestedPojo a un objeto Java que tiene un campo llamado nestedPojo de alguna Collection o tipo de matriz, donde ese tipo define dos campos llamados name y value , respectivamente.

Sin embargo, ha definido su tipo de Pojo como teniendo un campo

 NestedPojo nestedPojo; 

eso no es un tipo de matriz, ni un tipo de Collection . Gson no puede deserializar el JSON correspondiente para este campo.

En cambio, tienes 3 opciones:

  • Cambie su JSON para que coincida con el tipo esperado

     { "nestedPojo": { "name": null, "value": 42 } } 
  • Cambia tu tipo de Pojo para esperar una Collection o tipo de matriz

     List nestedPojo; // consider changing the name and using @SerializedName NestedPojo[] nestedPojo; 
  • Escriba y registre un deserializador personalizado para NestedPojo con sus propias reglas de análisis. Por ejemplo

     class Custom implements JsonDeserializer { @Override public NestedPojo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { NestedPojo nestedPojo = new NestedPojo(); JsonArray jsonArray = json.getAsJsonArray(); if (jsonArray.size() != 1) { throw new IllegalStateException("unexpected json"); } JsonObject jsonObject = jsonArray.get(0).getAsJsonObject(); // get only element JsonElement jsonElement = jsonObject.get("name"); if (!jsonElement.isJsonNull()) { nestedPojo.name = jsonElement.getAsString(); } nestedPojo.value = jsonObject.get("value").getAsInt(); return nestedPojo; } } Gson gson = new GsonBuilder().registerTypeAdapter(NestedPojo.class, new Custom()).create(); 
 class Pojo { NestedPojo nestedPojo; } 

en tu JSON tienes una matriz dePojo nested así que o cambias el código

  NestedPojo[] nestedPojo; 

o cambias la cadena json

 String json = "{\"nestedPojo\":{\"name\":null, \"value\":42}}";