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
}
]