Retrofit2 Android: ожидается BEGIN_ARRAY, но BEGIN_OBJECT в строке 1 столбец 2 путь $

Я знаю, что это не первый раз, когда кто-то спрашивает об этой проблеме, но с Retrofit2. Я не могу найти правильное решение моей проблемы. Я последовал онлайн-учебнику, и он работал отлично. Когда я применил тот же код к своей конечной точке, я получаю это исключение: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $ Я не знаю, как это решить.

Интерфейс:

public interface MyApiService {

// Is this right place to add these headers?
@Headers({"application-id: MY-APPLICATION-ID",
        "secret-key: MY-SECRET-KEY",
        "application-type: REST"})
@GET("Music")
Call<List<Music>> getMusicList();



Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(MySettings.REST_END_POINT)
        .addConverterFactory(GsonConverterFactory.create())
        .build();
}

Клиентский код:

MyApiService service = MyApiService.retrofit.create(MyApiService.class);
Call<List<Music>> call = service.getMusicList();
call.enqueue(new Callback<List<Music>>() {

    @Override
    public void onResponse(Call<List<Music>> call, Response<List<Music>> response) {
        Log.e("MainActivity", response.body().
    }

    @Override
    public void onFailure(Call<List<Music>> call, Throwable t) {
        Log.e("MainActivity", t.toString());
    }
});

Этот код работает с этой полезной нагрузкой:

[
{
    "login": "JakeWharton",
    "id": 66577,
    "avatar_url": "https://avatars.githubusercontent.com/u/66577?v=3",
    "gravatar_id": "",
    "url": "https://api.github.com/users/JakeWharton",
    "html_url": "https://github.com/JakeWharton",
    "followers_url": "https://api.github.com/users/JakeWharton/followers",
    "following_url": "https://api.github.com/users/JakeWharton/following{/other_user}",
    "gists_url": "https://api.github.com/users/JakeWharton/gists{/gist_id}",
    "starred_url": "https://api.github.com/users/JakeWharton/starred{/owner}{/repo}",
    "subscriptions_url": "https://api.github.com/users/JakeWharton/subscriptions",
    "organizations_url": "https://api.github.com/users/JakeWharton/orgs",
    "repos_url": "https://api.github.com/users/JakeWharton/repos",
    "events_url": "https://api.github.com/users/JakeWharton/events{/privacy}",
    "received_events_url": "https://api.github.com/users/JakeWharton/received_events",
    "type": "User",
    "site_admin": false,
    "contributions": 741
},
{....

но не с этим:

{
"offset": 0,
"data": [
    {
        "filename": "E743_1458662837071.mp3",
        "created": 1458662854000,
        "publicUrl": "https://api.backendless.com/dbb77803-1ab8-b994-ffd8-65470fa62b00/v1/files/music/E743_1458662837071.mp3",
        "___class": "Music",
        "description": "",
        "likeCount": 0,
        "title": "hej Susanne. ",
        "ownerId": "E743756F-E114-6892-FFE9-BCC8C072E800",
        "updated": null,
        "objectId": "DDD8CB3D-ED66-0D6F-FFA5-B14543ABC800",
        "__meta": "{\"relationRemovalIds\":{},\"selectedProperties\":[\"filename\",\"created\",\"publicUrl\",\"___class\",\"description\",\"likeCount\",\"title\",\"ownerId\",\"updated\",\"objectId\"],\"relatedObjects\":{}}"
    },
    {...

Класс My Music:

public class Music {

   private String ownerId;
   private String filename;
   private String title;
   private String description;
   private String publicUrl;
   private int likeCount;

   // Getters & Setters

}

Ответы

Ответ 1

Когда вы говорите: "Этот код работает с этой полезной нагрузкой:... но не с этим:...", который ожидал и как он должен работать. На самом деле сообщение об ошибке сообщает вам, что при преобразовании json в объект java вызов ожидал массив в json, но вместо этого получил объект.

Этот вызов:

@GET("Music")
Call<List<Music>> getMusicList();

ожидает список объектов Music, поэтому он работает с json:

[
  {
    "login": "JakeWharton",
    ...
  },
  ...
]

Поскольку сам json представляет собой массив ваших объектов Music (Retrofit может преобразовывать между json-массивами в java-списки). Для второго json у вас есть только объект, а не массив (обратите внимание на отсутствие [...]). Для этого вам нужно создать другой вызов с другой моделью, которая будет сопоставляться с этим json. Предположим, вы назвали модель MusicList. Вот как выглядел бы вызов:

@GET("Music")
Call<MusicList> getMusicList();

(Обратите внимание, что вам может потребоваться изменить имя метода, если вы хотите сохранить как первый вызов, так и этот).

Модель MusicList может выглядеть примерно так:

public class MusicList {
  @SerializedName("data")
  private List<Music> musics;
  // ...
}

Я предполагаю, что массив data представляет собой список объектов Music, но я заметил, что jsons совершенно разные. Возможно, вам придется адаптировать это также, но я думаю, что вы получили картину здесь.

Ответ 2

Я встречаюсь с этой проблемой. Поскольку playload - это объект вместо массива объектов. Поэтому я удаляю List.

Пример кода

UserAPI.java

public interface UserAPI {
    @GET("login/cellphone")
    Call<LoginResponse> login(@Query("phone") String phone, 
                              @Query("password") String password);
}

Код звонка

Retrofit retrofit = new Retrofit
        .Builder()
        .addConverterFactory(GsonConverterFactory.create())
        .baseUrl(Constant.CLOUD_MUSIC_API_BASE_URL)
        .build();

UserAPI userAPI = retrofit.create(UserAPI.class);

userAPI.login(phone, password).enqueue(new Callback<LoginResponse>() {
    @Override
    public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
        System.out.println("onResponse");
        System.out.println(response.body().toString());
    }

    @Override
    public void onFailure(Call<LoginResponse> call, Throwable t) {
        System.out.println("onFailure");
        System.out.println(t.fillInStackTrace());
    }
});

Ответ 3

private List<Message> messages = new ArrayList<>();

private void getInbox() {
        ApiInterface apiService =
                ApiClient.getClient().create(ApiInterface.class);

        Call<List<Message>> call = apiService.getInbox();
        call.enqueue(new Callback<List<Message>>() {
            @Override
            public void onResponse(Call<List<Message>> call, Response<List<Message>> response) {
                // clear the inbox
                messages.clear();

                // add all the messages
//                 messages.addAll(response.body());

                // TODO - avoid looping
                // the loop was performed to add colors to each message

                mAdapter.notifyDataSetChanged();
                swipeRefreshLayout.setRefreshing(false);
            }

            @Override
            public void onFailure(Call<List<Message>> call, Throwable t) {
                Toast.makeText(getApplicationContext(), "Unable to fetch json: " + t.getMessage(), Toast.LENGTH_LONG).show();
            }
        });
    }


import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class ApiClient {
    public static final String BASE_URL = "http://api.androidhive.info/json/";
    private static Retrofit retrofit = null;

    public static Retrofit getClient() {
        if (retrofit == null) {
            retrofit = new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }
        return retrofit;
    }
}


import com.keshav.gmailretrofitexampleworking.models.Message;

import java.util.List;

import retrofit2.Call;
import retrofit2.http.GET;

public interface ApiInterface {
    @GET("inbox.json")
    Call<List<Message>> getInbox();
}

=======================================================
package com.keshav.gmailretrofitexampleworking.models;

public class Message {
    private int id;
    private String from;
    private String subject;
    private String message;
    private String timestamp;
    private String picture;
    private boolean isImportant;
    private boolean isRead;
    private int color = -1;

    public Message() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getFrom() {
        return from;
    }

    public void setFrom(String from) {
        this.from = from;
    }

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public String getTimestamp() {
        return timestamp;
    }

    public void setTimestamp(String timestamp) {
        this.timestamp = timestamp;
    }

    public boolean isImportant() {
        return isImportant;
    }

    public void setImportant(boolean important) {
        isImportant = important;
    }

    public String getPicture() {
        return picture;
    }

    public void setPicture(String picture) {
        this.picture = picture;
    }

    public boolean isRead() {
        return isRead;
    }

    public void setRead(boolean read) {
        isRead = read;
    }

    public int getColor() {
        return color;
    }

    public void setColor(int color) {
        this.color = color;
    }
}

=======================================
Response
=========================================
[
    {
        "id": 1,
        "isImportant": false,
        "picture": "http://api.androidhive.info/json/google.png",
        "from": "Google Alerts",
        "subject": "Google Alert - android",
        "message": "Now android supports multiple voice recogonization",
        "timestamp": "10:30 AM",
        "isRead": false
    },
    {
        "id": 2,
        "isImportant": true,
        "picture": "",
        "from": "Amazon.in",
        "subject": "Apple Macbook Pro",
        "message": "Your Amazon.in Today Deal Get Macbook Pro",
        "timestamp": "9:20 AM",
        "isRead": false
    },
    {
        "id": 3,
        "isImportant": false,
        "picture": "http://api.androidhive.info/json/imdb.png",
        "from": "IMDB",
        "subject": "Check out top rated movies this week",
        "message": "Find out the top rated movies this week by editor choice",
        "timestamp": "9:15 AM",
        "isRead": false
    },
    {
        "id": 4,
        "isImportant": false,
        "picture": "https://scontent-frt3-1.xx.fbcdn.net/v/t1.0-1/p160x160/16298485_10208198778434887_8511207535440105240_n.jpg?oh=37c747b74709ff3bbdec6102c189cea5&oe=5929B733",
        "from": "Dinesh Reddy, Me",
        "subject": "Let get started",
        "message": "I started working on project proposals. Wanted to know",
        "timestamp": "9:00 AM",
        "isRead": true
    },
    {
        "id": 5,
        "isImportant": false,
        "picture": "",
        "from": "BookMyShow",
        "subject": "Will the lost boy find his family?",
        "message": "LION a movie that humanity needs. Online version report spam 97%",
        "timestamp": "8:55 AM",
        "isRead": false
    },
    {
        "id": 6,
        "isImportant": false,
        "picture": "",
        "from": "MakeMyTrip",
        "subject": "A joyous Train Ride in North East!",
        "message": "North East Package starting from Rs31,999/- only!",
        "timestamp": "8:10 AM",
        "isRead": false
    },
    {
        "id": 7,
        "isImportant": false,
        "picture": "",
        "from": "Disqus 14",
        "subject": "Re: Comment on Android Working with",
        "message": "Hey Ravi, after following your article I am getting this error",
        "timestamp": "8:10 AM",
        "isRead": true
    },
    {
        "id": 8,
        "isImportant": true,
        "picture": "",
        "from": "Evans, Ravi 3",
        "subject": "Advertisement Enquiry",
        "message": "Hello, I want to buy the advertising space on",
        "timestamp": "7:59 AM",
        "isRead": false
    },
    {
        "id": 9,
        "isImportant": false,
        "picture": "https://lh3.googleusercontent.com/9JKVlFDCj9VVIbI66z_JEXGXvQpUXc-EwXfDmYP9c-8Lwb6Bd_zZ9i6VygPpyXLj3PkNCtpmTvaKKw=w300-h300-rw-no",
        "from": "Karthik Tamada",
        "subject": "Call Me",
        "message": "I am going home because I am not feeling well!",
        "timestamp": "4:05 AM",
        "isRead": false
    },
    {
        "id": 10,
        "isImportant": false,
        "picture": "",
        "from": "Order-Update",
        "subject": "Your Amazon order #408-2878882019",
        "message": "Your Orders | Amazon.in shipment of pendrive",
        "timestamp": "5:00 AM",
        "isRead": true
    },
    {
        "id": 11,
        "isImportant": false,
        "picture": "",
        "from": "Instapage",
        "subject": "Lead notification from Instapage",
        "message": "Congratulations! You just aquired a new lead",
        "timestamp": "3:00 AM",
        "isRead": false
    },
    {
        "id": 12,
        "isImportant": false,
        "picture": "",
        "from": "Droid5",
        "subject": "Droid5 Android Project",
        "message": "Planning to start an android app for droid5",
        "timestamp": "5:00 AM",
        "isRead": false
    },
    {
        "id": 13,
        "isImportant": true,
        "picture": "",
        "from": "Gmail Team",
        "subject": "Gmail Business Suite",
        "message": "Your Gmail business suite is expiring today!",
        "timestamp": "Feb 20",
        "isRead": true
    },
    {
        "id": 14,
        "isImportant": false,
        "picture": "",
        "from": "Medium Daily Digest",
        "subject": "6 Things You need to know to Recover",
        "message": "Daily Digest Your daily three recommendations",
        "timestamp": "8:15 AM",
        "isRead": false
    },
    {
        "id": 15,
        "isImportant": false,
        "picture": "",
        "from": "Alex, Ravi 24",
        "subject": "Advertisement proposal to promote my android app",
        "message": "Hello Ravi, Thanks for your link. Here is the proposal",
        "timestamp": "Feb 17",
        "isRead": false
    }
]