Использование внутреннего объекта JSON ответа для преобразования его в POJO? - PullRequest
0 голосов
/ 25 августа 2018

Я хотел использовать конвертеры для мгновенного получения POJO из ответа, но корневой JSON-объект - это не тот объект, который я хочу десериализовать в POJO. На самом деле я хочу десериализовать два его родственных объекта в POJO.

Например, получить поток из нового API Twitch:

@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, и, поскольку он не может найти ни одного поля в этом классе, названного или помеченного как data и 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, и передавать только объекты data и pagination в Gson для десериализации. Но как я мог это сделать?

Ответы [ 2 ]

0 голосов
/ 25 августа 2018

Как говорится в вашем исключении

Ошибка ResponseBase: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: ожидаемый BEGIN_OBJECT, но был BEGIN_ARRAY в строке 1 столбца 10 path $ .data

он ожидает объект 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> (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
}
0 голосов
/ 25 августа 2018

JSON, который вы предоставили, в основном нуждается в трех классах.Они следующие.

public class TwitchStream {
    public Data[] data;
    public Pagination pagination;
}

public class Pagination {
    public String cursor;
}

public class Data {
    public String id;
    public String thumbnail_url;
    public String title;
    public String game_id;
    public String started_at;
    public String[] community_ids;
    public String user_id;
    public String language;
    public String viewer_count;
    public String type;
}

Надеюсь, теперь вы можете легко проанализировать ответ JSON с помощью Gson.Дайте мне знать, если это поможет!

...