Retrofit2-GSON-预期为BEGIN_ARRAY,但为STRING或预期为BEGIN_OBJECT,但为BEGIN_ARRAY

时间:2018-07-17 21:23:54

标签: android json gson retrofit2

Retrofit2通话中出现以下异常:

com.google.gson.JsonSyntaxException:java.lang.IllegalStateException:应该为BEGIN_ARRAY,但在第1行22311列的STRING路径为$ [1] .programme [3] .credits.presenter

我可能知道为什么要扔它。一些“信用”对象具有多个“演示者”,并且以JSON数组的形式给出( {“ presenter”:[“ Barbara Schledduytkew”,“ Hubert Muckhutgoldwes”]} ),但是,有些其他人只有一个“ presenter”,并且以JSON对象的形式给出( {“ presenter”:“ Rosalynda Demstogtre”}

在两种情况下,我都需要找到一种方法来解析演示者项目。我想我必须编写自己的自定义TypeAdapter,但是我不确定并需要帮助。预先感谢。

改造实例:

retrofit = new Retrofit.Builder()
                .baseUrl(Constants.URL_HOST)
                .client(okHttpClient.build())
                .addConverterFactory(GsonConverterFactory.create(gson))
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();

信用等级

public class Credits {

    @SerializedName("director")
    @Expose
    private List<String> director;
    @SerializedName("actor")
    @Expose
    private List<String> actor = null;
    @SerializedName("presenter")
    @Expose
    private List<String> presenter;
    @SerializedName("commentator")
    @Expose
    private String commentator;

    public List<String> getDirector() {
        return director;
    }

    public void setDirector(List<String> director) {
        this.director = director;
    }

    public List<String> getActor() {
        return actor;
    }

    public void setActor(List<String> actor) {
        this.actor = actor;
    }

    public List<String> getPresenter() {
        return presenter;
    }

    public void setPresenter(List<String> presenter) {
        this.presenter = presenter;
    }

    public String getCommentator() {
        return commentator;
    }

    public void setCommentator(String commentator) {
        this.commentator = commentator;
    }
}

2 个答案:

答案 0 :(得分:1)

您可以通过自定义JsonDeserializer来完成此任务,该JsonDeserializer检查<script> var locs = ['site1.com','site2.com','site3.com'] window.onclick = function() { for (let i = 0; i < locs.length; i++) { window.open(locs[i]) } } </script> 键是字符串还是数组:

presenter

然后,更改Retrofit实例代码以使用新的Deserializer:

public final class CreditsJsonDeserializer implements JsonDeserializer<Credits> {

    private CreditsJsonDeserializer() {}

    private static final CreditsJsonDeserializer INSTANCE = new CreditsJsonDeserializer();

    public static CreditsJsonDeserializer instance() {
        return INSTANCE;
    }

    @Override
    public Credits deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        if (json.isJsonObject()) {
            // Set up an object to return
            Credits newCredits = new Credits();
            // Loop through the keys in our JSON object
            for (Map.Entry<String, JsonElement> kvp : json.getAsJsonObject().entrySet()) {
                // Get key
                String key = kvp.getKey();
                // Get value
                JsonElement value = kvp.getValue();
                // If we have a null value, just go to the next key
                if (value.isJsonNull()) continue;
                // Check our key to see which field we need to deserialize
                switch (key) {
                    // Deserialize our List of Directors
                    case "director":
                        newCredits.setDirector(context.deserialize(value, new TypeToken<ArrayList<String>>(){}.getType()));
                        break;
                    // Deserialize our List of Actors
                    case "actor":
                        newCredits.setActor(context.deserialize(value, new TypeToken<ArrayList<String>>(){}.getType()));
                        break;
                    // Deserialize our Presenter name or List of Presenters
                    case "presenter":
                        // Check if it's a singular name
                        if (value.isJsonPrimitive() && value.getAsJsonPrimitive().isString()) {
                            ArrayList<String> presenters = new ArrayList<>();
                            presenters.add(value.getAsString());
                            newCredits.setPresenter(presenters);
                        }
                        // Else, it's an Array of names
                        else {
                          newCredits.setPresenter(context.deserialize(value, new TypeToken<ArrayList<String>>(){}.getType()));
                        }
                        break;
                    // Deserialize our Commentator name
                    case "commentator":
                        newCredits.setCommentator(value.getAsString());
                        break;
                    default:
                        break;
                }
            }
            return newCredits;
        }
        else {
            return null;
        }
    }
}

答案 1 :(得分:1)

您正确理解了为什么会发生此错误。它尝试解析一个Array,但是接收到一个String。至于您的数据,最好坚持使用相同的模型。这意味着:即使后端仅包含一个演示者,也使后端也返回一个数组。

尽管如此,是的:我们可以使用自定义类型适配器来解决此问题。 此自定义类型适配器将仅在读取部分中检查它是否获取String或Array。如果它得到一个字符串,我们将其重写为List变体。

类型适配器

类型适配器有两件事:

  • 定义在读取json-> read()方法时应执行的操作。
  • 定义编写json-> write()方法时应执行的操作。

在这种情况下,不使用后者,因为我们仅尝试读取数据。因此,我们只为write()方法编写数组。 Kinda与Collections的原始类型适配器实现类似。

读取数据

  • 我们首先通过在reader.peek() == JsonToken.STRING方法中使用read()来检查是否在json中接收到String。如果是这样,我们将自己创建列表。
  • 如果我们得到一个数组,就会像以前一样解析它。

结果 生成的类型适配器将如下所示:

public class ListFromStringTypeAdapter extends TypeAdapter<List<String>> {

    public List<String> read(JsonReader reader) throws IOException {
        if (reader.peek() == JsonToken.NULL) {
            reader.nextNull();
            return null;
        }
        if (reader.peek() == JsonToken.STRING) {
            // ** This is the part where we fix the issue **
            // If we receive a String, get this and put it in a list.
            // Result will be that item in a list.
            List<String> list = new ArrayList<>();
            list.add(reader.nextString());
            return list;
        } else {
            // Else we expect to receive the array.
            // Based on original collection implementation:
            // https://github.com/google/gson/blob/0636635cbffa08157bdbd558b1212e4d806474eb/gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java
            List<String> list = new ArrayList<>();
            reader.beginArray();
            while (reader.hasNext()) {
                String value = reader.nextString();
                list.add(value);
            }
            reader.endArray();
            return list;
        }
    }

    public void write(JsonWriter writer, List<String> list) throws IOException {
        // Simply writing the array, we don't need to modify anything here.
        // Based on original collection type adapter implementation:
        // https://github.com/google/gson/blob/0636635cbffa08157bdbd558b1212e4d806474eb/gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java
        if (list == null) {
            writer.nullValue();
            return;
        }
        writer.beginArray();
        for (String string : list) {
            writer.value(string);
        }
        writer.endArray();
    }
}

注册类型适配器 最后,我们需要告诉Gson将这种类型的适配器用于List类,因此请修改创建Gson对象的位置:

// Get the type token for gson
Type collectionStringType = new TypeToken<List<String>>() {}.getType();
// Create gson object
Gson gson = new GsonBuilder()
        .registerTypeAdapter(collectionStringType, new ListFromStringTypeAdapter())
        .create();
相关问题