首页 > 解决方案 > 使用响应的内部 JSON 对象将其转换为 POJO?

问题描述

我想使用转换器立即从响应中获取 POJO,但根 JSON 对象并不是我想要反序列化为 POJO 的对象。事实上,我想将它的 2 个兄弟对象反序列化为一个 POJO。

例如,从新的 Twitch API 获取流:

@GET("/helix/streams?first=1")
Call<TwitchStream> getLatestStreamsForGame(@Query("game_id") int gameId);

响应将是这样的:

{
  "data": [
    {
      "id": "26007494656",
      "user_id": "23161357",
      "game_id": "417752",
      "community_ids": [
        "5181e78f-2280-42a6-873d-758e25a7c313",
        "848d95be-90b3-44a5-b143-6e373754c382",
        "fd0eab99-832a-4d7e-8cc0-04d73deb2e54"
      ],
      "type": "live",
      "title": "Hey Guys, It's Monday - Twitter: @Lirik",
      "viewer_count": 32575,
      "started_at": "2017-08-14T16:08:32Z",
      "language": "en",
      "thumbnail_url": "https://static-cdn.jtvnw.net/previews-ttv/live_user_lirik-{width}x{height}.jpg"
    },
    ...
  ],
  "pagination": {
    "cursor": "eyJiIjpudWxsLCJhIjp7Ik9mZnNldCI6MjB9fQ=="
  }
}

这是我要存储流数据的类:

public class TwitchStream extends DataResponse{
    @SerializedName("id")
    private String id;
    @SerializedName("user_id")
    private String userId;
    @SerializedName("game_id")
    private String gameId;
    @SerializedName("community_ids")
    private List<String> communityIds;
    @SerializedName("type")
    private String type;
    @SerializedName("title")
    private String title;
    @SerializedName("viewer_count")
    private int viewerCount;
    @SerializedName("started_at")
    private String startedAt;
    @SerializedName("language")
    private String language;
    @SerializedName("thumbnail_url")
    private String thumbnailUrl;
    @SerializedName("cursor")
    private String paginationCursor;

    public TwitchStream(String id, String user_id, String game_id, List<String> community_ids, String type, String title, int viewer_count, String started_at, String language, String thumbnail_url, String paginationCursor) {
        this.id = id;
        this.userId = user_id;
        this.gameId = game_id;
        this.communityIds = community_ids;
        this.type = type;
        this.title = title;
        this.viewerCount = viewer_count;
        this.startedAt = started_at;
        this.language = language;
        this.thumbnailUrl = thumbnail_url;
        this.paginationCursor = paginationCursor;
    }

    public String getId() {
        return id;
    }

    public String getUserId() {
        return userId;
    }

    public String getGameId() {
        return gameId;
    }

    public List<String> getCommunityIds() {
        return communityIds;
    }

    public String getType() {
        return type;
    }

    public String getTitle() {
        return title;
    }

    public int getViewerCount() {
        return viewerCount;
    }

    public String getStartedAt() {
        return startedAt;
    }

    public String getLanguage() {
        return language;
    }

    public String getThumbnailUrl() {
        return thumbnailUrl;
    }

    public String getPaginationCursor() {
        return paginationCursor;
    }
}

我想将 的内容反序列化data到上面的 POJO 中,我还需要来自pagination. 我想使用GsonConverterFactory,但 Gson 想反序列化根 JSON 对象,并且由于它在此类中找不到任何名为dataand的字段pagination,因此它什么也没做。

我能做什么?一种解决方案是创建一个包装类,并以这种方式创建调用:

@GET("/helix/streams?first=1")
Call<Wrapper<TwitchStream>> getLatestStreamsForGame(@Query("game_id") int gameId);

但是这样我会得到一个例外:

ResponseBase failed: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 10 path $.data
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 10 path $.data
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:226)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:131)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:222)
    at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:39)
    at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:27)
    at retrofit2.ServiceMethod.toResponse(ServiceMethod.java:122)
    at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:217)
    at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:116)
    at okhttp3.RealCall$AsyncCall.execute(RealCall.java:153)
    at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
    at java.lang.Thread.run(Thread.java:818)
 Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 10 path $.data
    at com.google.gson.stream.JsonReader.beginObject(JsonReader.java:385)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:215)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:131) 
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:222) 
    at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:39) 
    at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:27) 
    at retrofit2.ServiceMethod.toResponse(ServiceMethod.java:122) 
    at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:217) 
    at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:116) 
    at okhttp3.RealCall$AsyncCall.execute(RealCall.java:153) 
    at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32) 
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) 
    at java.lang.Thread.run(Thread.java:818)
enter code here

另一个选项是JSONParser在 Gson 尝试反序列化根 JSON 对象之前使用,并且只将datapagination对象传递给 Gson 进行反序列化。但我怎么能这样做呢?

标签: javaandroidjsongsonretrofit2

解决方案


正如你的例外状态

ResponseBase 失败:com.google.gson.JsonSyntaxException:java.lang.IllegalStateException:预期 BEGIN_OBJECT 但在第 1 行第 10 列路径 $.data 处为 BEGIN_ARRAY

它等待 json 对象但遇到 json 数组。因为在您的 json 结构中,“数据”是一个数组:

"data": [ // data is a json array
    {
      "id": "26007494656",
      "user_id": "23161357",
      "game_id": "417752",
      "community_ids": [
        "5181e78f-2280-42a6-873d-758e25a7c313",
        "848d95be-90b3-44a5-b143-6e373754c382",
        "fd0eab99-832a-4d7e-8cc0-04d73deb2e54"
      ],
      "type": "live",
      "title": "Hey Guys, It's Monday - Twitter: @Lirik",
      "viewer_count": 32575,
      "started_at": "2017-08-14T16:08:32Z",
      "language": "en",
      "thumbnail_url": "https://static-cdn.jtvnw.net/previews-ttv/live_user_lirik-{width}x{height}.jpg"
    },
    ...
  ]

所以我建议你为你的 json 结构创建一个根对象,它包含List<Data>(数据是你当前的没有分页变量的 TwichStream 类)并且根对象包含另一个代表分页对象的类。

所以你有3个类如下:

@Data // comes from lombok
class RootClass {
    @SerializedName("data")
    private List<Data> datas;
    private Pagination pagination;
}

@Data
class Pagination {
    private String cursor;
}

@Data
class Data {
    // your current implementation without pagination field
}

推荐阅读