Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Gson fromJson throw a JsonSyntaxException: Expected BEGIN_OBJECT but was BEGIN_ARRAY?

Tags:

java

json

gson

(This post is meant to be a canonical question with a sample answer provided below.)


I'm trying to deserialize some JSON content into a custom POJO type with Gson#fromJson(String, Class).

This piece of code

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

throws the follow exception

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

Why can't Gson properly convert my JSON text to my POJO type?

like image 471
Sotirios Delimanolis Avatar asked Nov 10 '15 02:11

Sotirios Delimanolis


People also ask

What is Begin_object?

begin_object means the json response is an object which will look something like this {....} gson is one cool library that will provide us with cool tips in the form of errors while handling json responses.

Does Gson ignore extra fields?

3. Deserialize JSON With Extra Unknown Fields to Object. As you can see, Gson will ignore the unknown fields and simply match the fields that it's able to.

What does Gson toJson do?

Introduction. Gson is the main actor class of Google Gson library. It provides functionalities to convert Java objects to matching JSON constructs and vice versa. Gson is first constructed using GsonBuilder and then toJson(Object) or fromJson(String, Class) methods are used to read/write JSON constructs.


1 Answers

As the exception message states

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

while deserializing, Gson was expecting a JSON object, but found a JSON array. Since it couldn't convert from one to the other, it threw this exception.

The JSON format is described here. In short, it defines the following types: objects, arrays, strings, numbers, null, and the boolean values true and false.

In Gson (and most JSON parsers), the following mappings exist: a JSON string maps to a Java String; a JSON number maps to a Java Number type; a JSON array maps to a Collection type or an array type; a JSON object maps to a Java Map type or, typically, a custom POJO type (not mentioned previously); null maps to Java's null, and the boolean values map to Java's true and false.

Gson iterates through the JSON content that you provide and tries to deserialize it to the corresponding type you've requested. If the content doesn't match or can't be converted to the expected type, it'll throw a corresponding exception.

In your case, you provided the following JSON

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

At the root, this is a JSON object which contains a member named nestedPojo which is a JSON array. That JSON array contains a single element, another JSON object with two members. Considering the mappings defined earlier, you'd expect this JSON to map to a Java object which has a field named nestedPojo of some Collection or array type, where that types defines two fields named name and value, respectively.

However, you've defined your Pojo type as having a field

NestedPojo nestedPojo;

that is neither an array type, nor a Collection type. Gson can't deserialize the corresponding JSON for this field.

Instead, you have 3 options:

  • Change your JSON to match the expected type

    {
        "nestedPojo": {
            "name": null,
            "value": 42
        }
    }
    
  • Change your Pojo type to expect a Collection or array type

    List<NestedPojo> nestedPojo; // consider changing the name and using @SerializedName
    NestedPojo[] nestedPojo;
    
  • Write and register a custom deserializer for NestedPojo with your own parsing rules. For example

    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();
    
like image 152
Sotirios Delimanolis Avatar answered Oct 20 '22 04:10

Sotirios Delimanolis