Por que o Gson do Json lança um JsonSyntaxException: esperava algum tipo, mas era algum outro tipo?

(Esta postagem deve ser uma pergunta canônica com uma resposta de amostra fornecida abaixo.)


Estou tentando desserializar algum conteúdo JSON em um tipo POJO personalizado com Gson#fromJson(String, Class) .

Este pedaço 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; } 

lança a seguinte exceção

 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 que o Gson não pode converter corretamente meu texto JSON para o meu tipo POJO?

Como os estados da mensagem de exceção

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

durante a desserialização, o Gson esperava um object JSON, mas encontrou um array JSON. Como não conseguiu converter de um para o outro, isso gerou essa exceção.

O formato JSON é descrito aqui . Em suma, define os seguintes tipos: objects, matrizes, cadeias, números, valores null e valores booleanos como true e false .

No Gson (e na maioria dos analisadores JSON), existem os seguintes mapeamentos: uma string JSON mapeia para uma String Java; um número JSON mapeia para um tipo de Number Java; um array JSON mapeia para um tipo de Collection ou um tipo de matriz; um object JSON é mapeado para um tipo de Map Java ou, normalmente, um tipo POJO personalizado (não mencionado anteriormente); null mapeia para null de Java e os valores booleanos mapeiam para true e false Java.

Gson itera através do conteúdo JSON que você fornece e tenta desserializá-lo para o tipo correspondente que você solicitou. Se o conteúdo não corresponder ou não puder ser convertido para o tipo esperado, ele lançará uma exceção correspondente.

No seu caso, você forneceu o seguinte JSON

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

Na raiz, esse é um object JSON que contém um membro chamado nestedPojo que é uma matriz JSON. Esse array JSON contém um único elemento, outro object JSON com dois membros. Considerando os mapeamentos definidos anteriormente, você esperaria que esse JSON fosse nestedPojo para um object Java que possui um campo denominado nestedPojo de algum tipo Collection ou array, em que esses tipos definem dois campos denominados name e value , respectivamente.

No entanto, você definiu seu tipo Pojo como tendo um campo

 NestedPojo nestedPojo; 

que não é um tipo de matriz nem um tipo de Collection . O Gson não pode desserializar o JSON correspondente para esse campo.

Em vez disso, você tem 3 opções:

  • Altere seu JSON para corresponder ao tipo esperado

     { "nestedPojo": { "name": null, "value": 42 } } 
  • Altere seu tipo de Pojo para esperar um tipo de Collection ou array

     List nestedPojo; // consider changing the name and using @SerializedName NestedPojo[] nestedPojo; 
  • Escreva e registre um desserializador personalizado para o NestedPojo com suas próprias regras de análise. Por exemplo

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

no seu json você tem uma matriz de nestedPojo então ou você mudar o código

  NestedPojo[] nestedPojo; 

ou você muda a string do json

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