一尘不染

为什么Gson fromJson会引发JsonSyntaxException:预期为BEGIN_OBJECT但为BEGIN_ARRAY?

java

我正在尝试将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类型?


阅读 841

收藏
2020-02-26

共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,以及布尔值truefalse

在Gson(以及大多数JSON解析器)中,存在以下映射:JSON字符串映射到Java String;JSON数字映射为Java Number类型;JSON数组映射到Collection类型或数组类型;JSON对象映射到Java Map类型,或者通常映射到自定义POJO类型(前面没有提到);null映射到Javanull,布尔值映射到Java的truefalse

Gson遍历你提供的JSON内容,并尝试将其反序列化为你请求的相应类型。如果内容不匹配或无法转换为预期的类型,它将引发相应的异常。

就你而言,你提供了以下JSON

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

从根开始,这是一个JSON对象,其中包含一个名为nestedPojoJSON数组的成员。该JSON数组包含一个元素,另一个具有两个成员的JSON对象。考虑到之前定义的映射,你希望此JSON映射到Java对象,该对象具有一个名为nestedPojosome 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();
2020-02-26