Парсинг ответа API в LiveData с помощью Retrofit - PullRequest
0 голосов
/ 14 декабря 2018

Я пытаюсь создать приложение с GithubBrowserSample от Google здесь в качестве архитектуры MVVM.Пример имеет дело с Github API для получения данных.API возвращает список объектов, например:

[
              {
                "login": "obenland",
                "id": 1398304,
                "node_id": "MDQ6VXNlcjEzOTgzMDQ=",
         "site_admin": false,
        ...
                "contributions": 234
              },
              {
                "login": "davidakennedy",
                "id": 1473618,
                "node_id": "MDQ6VXNlcjE0NzM2MTg=",
                "avatar_url": "https://avatars1.githubusercontent.com/u/1473618?v=4",
                "gravatar_id": "",
                "url": "https://api.github.com/users/davidakennedy",
                "html_url": "https://github.com/davidakennedy",
                 ...
                "type": "User",
                "site_admin": false,
                "contributions": 95
              },
    ]

Но мой API приложения возвращает список внутри JSONObject следующим образом:

{"data":[{"id":41,"title":"Hey",...]}

Поэтому Retrofit не может проанализировать ответпо причине ошибки: Expected BEGIN_ARRAY but was BEGIN_OBJECT

Вызов API:

@GET("user/photos")
LiveData<ApiResponse<List<Photo>>> getPhotos();

Это класс ApiResponse:

public class ApiResponse<T> {
    private static final Pattern LINK_PATTERN = Pattern
            .compile("<([^>]*)>[\\s]*;[\\s]*rel=\"([a-zA-Z0-9]+)\"");
    private static final Pattern PAGE_PATTERN = Pattern.compile("\\bpage=(\\d+)");
    private static final String NEXT_LINK = "next";
    public final int code;
    @Nullable
    public final T body;
    @Nullable
    public final String errorMessage;
    @NonNull
    public final Map<String, String> links;

    public ApiResponse(Throwable error) {
        code = 500;
        body = null;
        errorMessage = error.getMessage();
        links = Collections.emptyMap();
    }

    public ApiResponse(Response<T> response) {

        code = response.code();
        if(response.isSuccessful()) {
            body = response.body();
            errorMessage = null;
        } else {
            String message = null;
            if (response.errorBody() != null) {
                try {
                    message = response.errorBody().string();
                } catch (IOException ignored) {
                    Timber.e(ignored, "error while parsing response");
                }
            }
            if (message == null || message.trim().length() == 0) {
                message = response.message();
            }
            errorMessage = message;
            body = null;
        }
        String linkHeader = response.headers().get("link");
        if (linkHeader == null) {
            links = Collections.emptyMap();
        } else {
            links = new ArrayMap<>();
            Matcher matcher = LINK_PATTERN.matcher(linkHeader);

            while (matcher.find()) {
                int count = matcher.groupCount();
                if (count == 2) {
                    links.put(matcher.group(2), matcher.group(1));
                }
            }
        }
    }

Это LiveDataCallAdapterFactory:

public class LiveDataCallAdapterFactory extends CallAdapter.Factory {

    @Override
    public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {

        if (getRawType(returnType) != LiveData.class) {
            return null;
        }
        Type observableType = getParameterUpperBound(0, (ParameterizedType) returnType);
        Class<?> rawObservableType = getRawType(observableType);
        if (rawObservableType != ApiResponse.class) {
            throw new IllegalArgumentException("type must be a resource");
        }
        if (! (observableType instanceof ParameterizedType)) {
            throw new IllegalArgumentException("resource must be parameterized");
        }

        Type bodyType = getParameterUpperBound(0, (ParameterizedType) observableType);
        return new LiveDataCallAdapter<>(bodyType);
    }
}

А вот как строится Retrofit:

return new Retrofit.Builder()
        .baseUrl("http://api.*******.com/")
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(new LiveDataCallAdapterFactory())
        .build()
        .create(GithubService.class);
...