Gson:反序列化可以是单个对象或数组的对象

时间:2017-05-09 11:04:51

标签: android json gson

我正在使用Gson解析某个API的JSON响应。 一切都运行正常,但现在似乎响应的一个字段可以以数组形式或单个元素形式出现,以便我得到com.google.gson.JsonSyntaxException: java.lang.IllegalStateException:

在这里,您可以看到导致问题的两个JSON版本的片段:

版本1

{
    "Notes": {
        "Note": [
            {
                "key": "disruption-message",
                "section": "high",
                "priority": "1",
                "message": "The battery consumption raised suddenly."
            },
            {
                "key": "disruption-message",
                "section": "low",
                "priority": "2",
                "message": "The power on the converter might be too high."
            }
        ]
    }
}

版本2

{
    "Notes": {
        "Note": {
            "key": "medium",
            "section": "low",
            "priority": "1",
            "message": "Life time for the battery will expire soon"
        }
    }
}

要解析 VERSION 1 ,我使用以下类:

public class Notes implements Serializable {

    @SerializedName("Note")
    @Expose
    private List<Note> note = null;

    public List<Note> getNote() {
        return note;
    }

    public void setNote(List<Note> note) {
        this.note = note;
    }

}

这适用于 VERSION 1 ,但当它发现JSON响应的一部分与 VERSION 2 匹配时,当然它会给出:

com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT 

如何对其进行反序列化Notes,无论其格式如何?

2 个答案:

答案 0 :(得分:4)

这个问题属于最着名的与Gson相关的问题组之一,我猜,并且设计不当的JSON响应受到了伤害。您可以在此处找到完全解决方案:Make GSON accept single objects where it expects arrays。拥有该类型的适配器工厂后,您可以注释这些映射:

final class ResponseV1 {

    @SerializedName("Notes")
    final NotesWrapperV1 notes = null;

}
final class NotesWrapperV1 {

    @SerializedName("Note")
    @JsonAdapter(AlwaysListTypeAdapterFactory.class)
    final List<Note> notes = null;

}
final class Note {

    final String key = null;
    final String section = null;
    final String priority = null;
    final String message = null;

}

IMO,你可以继续前进,只需删除内包装类。

final class ResponseV2 {

    @SerializedName("Notes")
    @JsonAdapter(NestedNotesTypeAdapterFactory.class)
    final List<Note> notes = null;

}

NestedNotesTypeAdapterFactory的实施方式如下:

final class NestedNotesTypeAdapterFactory
        implements TypeAdapterFactory {

    private static final TypeToken<List<Note>> noteListTypeToken = new TypeToken<List<Note>>() {
    };

    private NestedNotesTypeAdapterFactory() {
    }

    @Override
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
        // Just add the factory method to AlwaysListTypeAdapterFactory and let it just return the class singleton (the factory is stateless, so it can be constructed once)
        final TypeAdapter<List<Note>> noteListTypeAdapter = getAlwaysListTypeAdapterFactory().create(gson, noteListTypeToken);
        final TypeAdapter<List<Note>> nestedNotesTypeAdapter = new NestedNotesTypeAdapter(noteListTypeAdapter);
        @SuppressWarnings("unchecked")
        final TypeAdapter<T> typeAdapter = (TypeAdapter<T>) nestedNotesTypeAdapter;
        return typeAdapter;
    }

    private static final class NestedNotesTypeAdapter
            extends TypeAdapter<List<Note>> {

        private final TypeAdapter<List<Note>> noteListTypeAdapter;

        private NestedNotesTypeAdapter(final TypeAdapter<List<Note>> noteListTypeAdapter) {
            this.noteListTypeAdapter = noteListTypeAdapter;
        }

        @Override
        public void write(final JsonWriter out, final List<Note> value) {
            throw new UnsupportedOperationException();
        }

        @Override
        public List<Note> read(final JsonReader in)
                throws IOException {
            // "Unwrap" the Note property here
            in.beginObject();
            List<Note> notes = null;
            while ( in.hasNext() ) {
                final String name = in.nextName();
                switch ( name ) {
                case "Note":
                    // If we've reached the Note property -- just read the list
                    notes = noteListTypeAdapter.read(in);
                    break;
                default:
                    throw new MalformedJsonException("Unrecognized " + name + " at " + in);
                }
            }
            in.endObject();
            return notes;
        }

    }
}

两种实现的测试用例:

for ( final String resource : ImmutableList.of("version-1.json", "version-2.json") ) {
    System.out.println(resource);
    try ( final JsonReader jsonReader = getPackageResourceJsonReader(Q43868120.class, resource) ) {
        final ResponseV1 response = gson.fromJson(jsonReader, ResponseV1.class);
        for ( final Note note : response.notes.notes ) {
            System.out.println(note.message);
        }
    }
}
for ( final String resource : ImmutableList.of("version-1.json", "version-2.json") ) {
    System.out.println(resource);
    try ( final JsonReader jsonReader = getPackageResourceJsonReader(Q43868120.class, resource) ) {
        final ResponseV2 response = gson.fromJson(jsonReader, ResponseV2.class);
        for ( final Note note : response.notes ) {
            System.out.println(note.message);
        }
    }
}

两者产生以下内容:

  

版本1.json
  电池消耗突然升高   转换器上的电源可能太高   版本2.json
  电池的使用寿命很快就会到期

答案 1 :(得分:0)

您遇到的问题是GSON期待一个数组,但JSON是对象的序列化。

基本上,GSON期待&#39; [&#39;但找到了&#39; {&#39;

因此,您应修改版本2的代码,以将JSON反序列化为单个Note对象。

此外,您应该查看两个JSON版本。

根据GSON,您的VERSION 2 JSON稍有效 您有一个Note对象列表 必须表示为
"Note"[ notes... ]

这正是版本1中的内容。

我建议将您的第2版JSON更改为类似的

"Notes"{ "Note":[ whatever note objects you have go in here. ] }

如果改变JSON不在您的权力范围内, 然后考虑为与此类似的案件开设另一个班级。

您应该使用@SerializedNames和Note对象创建新类,然后使用它来反序列化json。