为啥 Gson fromJson 会抛出 JsonSyntaxException: Expected BEGIN_OBJECT but was BEGIN_ARRAY?

Posted

技术标签:

【中文标题】为啥 Gson fromJson 会抛出 JsonSyntaxException: Expected BEGIN_OBJECT but was BEGIN_ARRAY?【英文标题】:Why does Gson fromJson throw a JsonSyntaxException: Expected BEGIN_OBJECT but was BEGIN_ARRAY?为什么 Gson fromJson 会抛出 JsonSyntaxException: Expected BEGIN_OBJECT but was BEGIN_ARRAY? 【发布时间】:2021-12-17 02:48:41 【问题描述】:

(这篇文章是canonical question,下面提供了一个示例答案。)


我正在尝试使用Gson#fromJson(String, Class) 将一些 JSON 内容反序列化为自定义 POJO 类型。

这段代码

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 格式描述为here。简而言之,它定义了以下类型:对象、数组、字符串、数字、null,以及布尔值truefalse

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

Gson 遍历您提供的 JSON 内容并尝试将其反序列化为您请求的相应类型。如果内容不匹配或无法转换为期望的类型,则会抛出相应的异常。

在您的情况下,您提供了以下 JSON


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

在根,这是一个 JSON 对象,其中包含一个名为 nestedPojo 的成员,它是一个 JSON 数组。该 JSON 数组包含一个元素,即另一个具有两个成员的 JSON 对象。考虑到前面定义的映射,您希望这个 JSON 映射到一个 Java 对象,该对象具有一个名为 nestedPojo 的字段,该字段属于某个 Collection 或数组类型,其中该类型定义了两个名为 namevalue 的字段,分别。

但是,您已将 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";

【讨论】:

以上是关于为啥 Gson fromJson 会抛出 JsonSyntaxException: Expected BEGIN_OBJECT but was BEGIN_ARRAY?的主要内容,如果未能解决你的问题,请参考以下文章

Gson.fromJson没有处理错误的响应

Gson fromJson() 用法

Json数据交换一Gson

json解析之gson

gson如何转化json数组

Gson使用