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

问题描述 投票:0回答:3

我正在使用 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,但是当它找到与 VERSION 2 匹配的 JSON 响应的一部分时,它当然会给出:

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

我怎样才能让它反序列化

Notes
,无论它们有什么格式?

android json gson
3个回答
5
投票

我猜这个问题属于最著名的 Gson 相关问题组之一,设计不当的 JSON 响应会造成伤害。您可以在这里找到精确解决方案:使 GSON 接受需要数组的单个对象。一旦您拥有该类型的适配器工厂,您就可以像这样注释您的映射:

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
电池的使用寿命即将到期


0
投票

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

基本上,GSON 期望 '[' 但发现 '{'

因此,您应该修改 version2 的代码,将 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。


0
投票

我找到了一个快速排序的解决方案,方法是在模型类中使用参数作为

Any
,然后将其序列化。

   private fun serializableToList(obj: Any): List<Pojo> {
    val pojoList: MutableList<Pojo> = mutableListOf()
    when (obj) {
        is List<*> -> {
            obj.forEach {
                val pojoObject = Gson().fromJson(Gson().toJson(it), Pojo::class.java)
                pojoList.add(pojoObject)
            }
        }

        else -> {
            val pojoObject = Gson().fromJson(Gson().toJson(obj), Pojo::class.java)
            pojoList.add(pojoObject)
        }
    }
    return pojoList
}
© www.soinside.com 2019 - 2024. All rights reserved.