Модернізація очікувалася BEGIN_OBJECT, але була BEGIN_ARRAY


79

Я досить новачок в аналізі JSON, я використовую бібліотеку модернізації 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 стовпець2

Що тут не так?

Відповіді:


168

Зараз ви аналізуєте відповідь так, ніби вона була відформатована так:

{
  "contacts": [
    { .. }
  ]
}

Виняток повідомляє вам про те, що ви очікуєте об’єкт у кореневій системі, але реальні дані насправді є масивом. Це означає, що вам потрібно змінити тип на масив.

Найпростіший спосіб - просто використовувати список як прямий тип у зворотному дзвінку:

@GET("/users.json")
void contacts(Callback<List<User>> cb);

2
@AzlanJamal так само
Дуглас Мескіта

15

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()щоб отримувати список об’єктів

Деякі посилання на вищезазначені випадки:

випадок 1: Розбір відповіді масиву json , випадок 2: Розбір відповіді об’єкта json , змішаний: Розбір масиву json всередині іншого об’єкта json


хай, я намагаюся отримати запит на отримання даних у третьому форматі, але я постійно отримую ту саму помилку, будь ласка, допоможіть мені
Swapna

будь-ласка, надайте більше інформації про свій код, а також переконайтесь, що написання імені масиву у класі моделі точно збігається з відповіддю
Navneet Krishna

8

у вашому інтерфейсі замінити

@GET("/users.json")
void contacts(Callback<Contacts> cb);

За цим кодом

@GET("/users.json")
void contacts(Callback<List<Contacts>> cb);

1
здається, це та сама відповідь мінус опис, який розмістив Джейк Уортон.
Пітер

Це правда, у мене була та ж помилка, і перед тим, як змінити pojo, я вирішив створити arrayList і отримати той самий результат
Francisco MEza

1

Перетворіть його у список.

Нижче наведено приклад:

BenchmarksListModel_v1[] benchmarksListModel = res.getBody().as(BenchmarksListModel_v1[].class);

1

Вихідний код працює

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();
            }
        });

я пробував так, але отримую response.body () як [] порожній .... немає даних, будь ласка, допоможіть мені
Swapna

0

Використовуючи MPV , у своєму десериалізаторі поставте це

JsonObject obj = new JsonObject();
obj.add("data", json);

JsonArray data  = obj.getAsJsonObject().getAsJsonArray("data");

Будь ласка, надайте пояснення, чому це вирішує проблему
sorak

Якщо відповідь сервера має формат JSON, подібний до цього {[..]}, тоді модернізація не може повторюватись правильно, на JSON потрібно розмістити префікс, як пропонує Джейк Уортон. остаточний JSON буде таким {"data":[..]}і проблема вирішена.
JoseDuin

0

Стек тут - 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>>
}

І код фрагмента сайту виклику, який використовує послугу:

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 замість рядків.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.