Дооснащение Ожидаемое BEGIN_OBJECT, но BEGIN_ARRAY
Я новичок в разборе JSON, я использую библиотеку Retrofit Square и столкнулся с этой проблемой.
Я пытаюсь разобрать этот ответ JSON:
[
{
"id": 3,
"username": "jezer",
"regid": "oiqwueoiwqueoiwqueoiwq",
"url": "http:\/\/192.168.63.175:3000\/users\/3.json"
},
{
"id": 4,
"username": "emulator",
"regid": "qwoiuewqoiueoiwqueoq",
"url": "http:\/\/192.168.63.175:3000\/users\/4.json"
},
{
"id": 7,
"username": "test",
"regid": "ksadqowueqiaksj",
"url": "http:\/\/192.168.63.175:3000\/users\/7.json"
}
]
Вот мои модели:
public class Contacts {
public List<User> contacts;
}
...
public class User {
String username;
String regid;
@Override
public String toString(){
return(username);
}
}
мой интерфейс:
public interface ContactsInterface {
@GET("/users.json")
void contacts(Callback<Contacts> cb);
}
мой метод успеха:
@Override
public void success(Contacts c, Response r) {
List<String> names = new ArrayList<String>();
for (int i = 0; i < c.contacts.size(); i++) {
String name = c.contacts.get(i).toString();
Log.d("Names", "" + name);
names.add(name);
}
ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, names);
mSentTo.setAdapter(spinnerAdapter);
}
Когда я использую его в моем методе успеха, он выдает ошибку
Ожидаемый BEGIN_OBJECT, но был BEGIN_ARRAY в строке 1 column2
Что здесь не так?
Ответы
Ответ 1
Теперь вы обрабатываете ответ так, как будто он был отформатирован следующим образом:
{
"contacts": [
{ .. }
]
}
Исключение указывает вам, что вы ожидаете объект в корне, но реальные данные на самом деле являются массивом. Это означает, что вам нужно изменить тип как массив.
Самый простой способ - просто использовать список в качестве прямого типа в обратном вызове:
@GET("/users.json")
void contacts(Callback<List<User>> cb);
Ответ 2
в вашем интерфейсе
замените
@GET("/users.json")
void contacts(Callback<Contacts> cb);
В этом коде
@GET("/users.json")
void contacts(Callback<List<Contacts>> cb);
Ответ 3
dependencies used :
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
Ответы json могут быть array response
или object response
или даже комбинацией обоих. Смотрите следующие три случая
Case 1: Parsing a json array response
(случай OP)
Этот случай относится к тем json responses
которые имеют форму [{...} ,{...}]
Например
[
{
"id": 3,
"username": "jezer",
"regid": "oiqwueoiwqueoiwqueoiwq",
"url": "http:\/\/192.168.63.175:3000\/users\/3.json"
},
.
.
]
Сначала создайте класс модели для этого массива или просто перейдите на jsonschema2pojo и автоматически сгенерируйте его, как показано ниже
Contacts.java
public class Contacts {
@SerializedName("id")
@Expose
private Integer id;
@SerializedName("username")
@Expose
private String username;
@SerializedName("regid")
@Expose
private String regid;
@SerializedName("url")
@Expose
private String url;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getRegid() {
return regid;
}
public void setRegid(String regid) {
this.regid = regid;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
ContactsInterface
В этом случае вы должны вернуть список объектов, как показано ниже
public interface ContactsInterface {
@GET("/users.json")
Call<List<Contacts>> getContacts();
}
Затем сделайте retrofit2
вызов вроде следующего
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("baseurl_here")
.addConverterFactory(GsonConverterFactory.create())
.build();
ContactsInterface request = retrofit.create(ContactsInterface.class);
Call<List<Contacts>> call = request.getContacts();
call.enqueue(new Callback<List<Contacts>>() {
@Override
public void onResponse(Call<List<Contacts>> call, Response<List<Contacts>> response) {
Toast.makeText(MainActivity.this,response.body().toString(),Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(Call<List<Contacts>> call, Throwable t) {
Log.e("Error",t.getMessage());
}
});
response.body()
выдаст вам список объектов
ВЫ МОЖЕТЕ ПРОВЕРИТЬ СЛЕДУЮЩИЕ ДВЕ СЛУЧАЯ ДЛЯ ССЫЛКИ
Case 2: Parsing a json object response
Этот случай относится к тем ответам json, которые имеют вид {..}
Например
{
"id": 3,
"username": "jezer",
"regid": "oiqwueoiwqueoiwqueoiwq",
"url": "http:\/\/192.168.63.175:3000\/users\/3.json"
}
Здесь мы имеем тот же object
в примере выше. Таким образом, класс модели будет таким же, но, как и в приведенном выше примере, у нас нет массива этих объектов - только один отдельный объект, и, следовательно, нам не нужно анализировать его как список.
Поэтому внесите следующие изменения в object response
public interface ContactsInterface {
@GET("/users.json")
Call<Contacts> getContacts();
}
Затем сделайте retrofit2
вызов вроде следующего
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("baseurl_here")
.addConverterFactory(GsonConverterFactory.create())
.build();
ContactsInterface request = retrofit.create(ContactsInterface.class);
Call<Contacts> call = request.getContacts();
call.enqueue(new Callback<Contacts>() {
@Override
public void onResponse(Call<Contacts> call, Response<Contacts> response) {
Toast.makeText(MainActivity.this,response.body().toString(),Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(Call<Contacts> call, Throwable t) {
Log.e("Error",t.getMessage());
}
});
response.body()
даст вам объект
Вы также можете проверить типичную ошибку при анализе ответа объекта json: "ожидается begin_array, но был begin_object"
Case 3: Parsing a json array inside json object
Этот случай применяется к тем json responses
которые имеют форму {"array_name":[{...} ,{...}]}
Например
{
"contacts":
[
{
"id": 3,
"username": "jezer",
"regid": "oiqwueoiwqueoiwqueoiwq",
"url": "http:\/\/192.168.63.175:3000\/users\/3.json"
}
]
}
Вам понадобятся два модельных класса, поскольку у нас есть два объекта (один снаружи и один внутри массива). Создайте его, как показано ниже
ContactWrapper
public class ContactWrapper {
@SerializedName("contacts")
@Expose
private List<Contacts> contacts = null;
public List<Contacts> getContacts() {
return contacts;
}
public void setContacts(List<Contacts> contacts) {
this.contacts = contacts;
}
}
Вы можете использовать Contacts.java
сгенерированный выше для объектов списка (сгенерированный для случая 1)
Поэтому внесите следующие изменения в object response
public interface ContactsInterface {
@GET("/users.json")
Call<ContactWrapper> getContacts();
}
Затем сделайте retrofit2
вызов вроде следующего
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("baseurl_here")
.addConverterFactory(GsonConverterFactory.create())
.build();
ContactsInterface request = retrofit.create(ContactsInterface.class);
Call<ContactWrapper> call = request.getContacts();
call.enqueue(new Callback<ContactWrapper>() {
@Override
public void onResponse(Call<ContactWrapper> call, Response<ContactWrapper> response) {
Toast.makeText(MainActivity.this,response.body().getContacts().toString(),Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(Call<ContactWrapper> call, Throwable t) {
Log.e("Error",t.getMessage());
}
});
Здесь отличие от случая 1 заключается в том, что мы должны использовать response.body().getContacts()
вместо response.body()
чтобы получить список объектов.
Некоторые ссылки для вышеупомянутых случаев:
case 1: синтаксический анализ ответа массива json, case 2: синтаксический анализ ответа объекта json, смешанный: синтаксический анализ массива json внутри другого объекта json
Ответ 4
Преобразуйте его в список.
Ниже приведен пример:
BenchmarksListModel_v1[] benchmarksListModel = res.getBody().as(BenchmarksListModel_v1[].class);
Ответ 5
Работа с исходным кодом
https://drive.google.com/open?id=0BzBKpZ4nzNzUVFRnVVkzc0JabUU
public interface ApiInterface {
@GET("inbox.json")
Call<List<Message>> getInbox();
}
call.enqueue(new Callback<List<Message>>() {
@Override
public void onResponse(Call<List<Message>> call, Response<List<Message>> response) {
YourpojoClass.addAll(response.body());
mAdapter.notifyDataSetChanged();
}
@Override
public void onFailure(Call<List<Message>> call, Throwable t) {
Toast.makeText(getApplicationContext(), "Unable to fetch json: " + t.getMessage(), Toast.LENGTH_LONG).show();
}
});
Ответ 6
Используя MPV, в Deserializer, поставьте это
JsonObject obj = new JsonObject();
obj.add("data", json);
JsonArray data = obj.getAsJsonObject().getAsJsonArray("data");
Ответ 7
Здесь мы используем стек Kotlin, Retrofit2, RxJava, и мы переходим к этому из обычных методов Call
.
Созданный мной сервис генерировал com.google.gson.JsonSyntaxException
и java.lang.IllegalStateException
с сообщением:
Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column2
Но все ответы, которые я мог найти, говорили, что это было вызвано отсутствием типа массива в сервисе, что я уже сделал. Мой сервис Kotlin выглядел так:
// Data class. Retrofit2 & Gson can deserialize this. No extra code needed.
data class InventoryData(
val productCode: String,
val stockDate: String,
val availCount: Int,
val totalAvailCount: Int,
val inventorySold: Int,
val closed: Boolean
)
// BROKEN SERVICE. Throws com.google.gson.JsonSyntaxException
// Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column2
interface InventoryService {
@GET("getInventoryData/{storeId}")
fun getInventoryData(@Path("storeId") storeId: String,
@Query("startDate") startDate: String,
@Query("endDate") endDate: String) :
Result<Single<List<InventoryData>>>
}
Проблема была в Result
, который я вставил, когда использовал более раннее решение на основе Call
.
Удаление это решило проблему. Мне также пришлось изменить сигнатуру двух методов обработки ошибок на моем сайте вызова для службы:
/// WORKING SERVICE
interface InventoryService {
@GET("getInventoryData/{storeId}")
fun getInventoryData(@Path("storeId") storeId: String,
@Query("startDate") startDate: String,
@Query("endDate") endDate: String) :
Single<List<InventoryData>>
}
И фрагмент кода call-сайта, который использует сервис:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.disposables
.add(viewModel.ratsService.getInventoryData(it, fromDate, toDate)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(this::successResult, this::failureResult))
}
}
private fun failureResult(error: Throwable) {
when (error) {
is HttpException -> { if (error.code() == 401) {
textField.text = "Log in required!" } }
else -> textField.text = "Error: $error."
}
}
/// Had to change to this from previous broken
/// one that took 'Result<List<InventoryData>>'
private fun successResult(result: List<InventoryData>) {
textField.text = result.toString()
}
Обратите внимание, что приведенный выше код был немного изменен. В частности, я использовал Retrofit2 ConverterFactory
чтобы даты могли передаваться как объекты OffsetDateTime вместо строк.