Почему Gson из Json выдает исключение JsonSyntaxException: ожидаемый BEGIN_OBJECT, но был BEGIN_ARRAY?
(Этот пост задуман как канонический вопрос с примером ответа, приведенным ниже.)
Я пытаюсь десериализовать некоторое содержимое JSON в пользовательский тип POJO с помощью Gson#fromJson(String, Class)
.
Этот фрагмент кода
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;
}
выдает следующее исключение
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
Почему Gson не может правильно преобразовать мой текст JSON в мой тип POJO?
Переведено автоматически
Ответ 1
Как указано в сообщении об исключении
Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 16 path $.nestedPojo
во время десериализации Gson ожидал объект JSON, но обнаружил массив JSON. Поскольку он не смог преобразовать из одного в другой, он выдал это исключение.
Формат JSON описан здесь. Короче говоря, он определяет следующие типы: объекты, массивы, строки, числа, null
и логические значения true
и false
.
В Gson (и большинстве парсеров JSON) существуют следующие сопоставления: строка JSON сопоставляется с Java String
; число JSON сопоставляется с типом Java Number
; массив JSON сопоставляется с Collection
типом или типом массива; объект JSON сопоставляется с типом Java Map
или, как правило, пользовательским типом POJO (не упоминалось ранее); null
сопоставляется с типом Java null
, а логические значения сопоставляются с типом Java true
и false
.
Gson выполняет итерацию по предоставляемому вами контенту JSON и пытается десериализовать его до соответствующего запрошенного вами типа. Если содержимое не соответствует или не может быть преобразовано в ожидаемый тип, это вызовет соответствующее исключение.
В вашем случае вы предоставили следующий JSON
{
"nestedPojo": [
{
"name": null,
"value": 42
}
]
}
В корне это объект JSON, который содержит элемент с именем nestedPojo
который является массивом JSON. Этот массив JSON содержит один элемент, другой объект JSON с двумя элементами. Учитывая сопоставления, определенные ранее, можно было бы ожидать, что этот JSON будет отображаться в объект Java, у которого есть поле с именем nestedPojo
некоторого Collection
типа или массива, где этот тип определяет два поля с именами name
и value
соответственно.
Однако вы определили свой Pojo
тип как имеющий поле
NestedPojo nestedPojo;
это ни тип массива, ни Collection
тип. Gson не может десериализовать соответствующий JSON для этого поля.
Вместо этого у вас есть 3 варианта:
Измените свой JSON, чтобы он соответствовал ожидаемому типу
{
"nestedPojo": {
"name": null,
"value": 42
}
}Измените свой
Pojo
тип, чтобы ожидатьCollection
или тип массиваList<NestedPojo> nestedPojo; // consider changing the name and using @SerializedName
NestedPojo[] nestedPojo;Напишите и зарегистрируйте пользовательский десериализатор для
NestedPojo
с вашими собственными правилами синтаксического анализа. Напримерclass Custom implements JsonDeserializer<NestedPojo> {
@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();
Ответ 2
class Pojo {
NestedPojo nestedPojo;
}
в вашем json у вас есть массив nestedPojo
так что либо вы меняете код
NestedPojo[] nestedPojo;
или вы меняете строку json
String json = "{\"nestedPojo\":{\"name\":null, \"value\":42}}";