Nachrüstung BEGIN_OBJECT erwartet, aber BEGIN_ARRAY

78

Ich bin ziemlich neu in der JSON-Analyse. Ich verwende die Retrofit-Bibliothek von Square und bin auf dieses Problem gestoßen.

Ich versuche, diese JSON-Antwort zu analysieren:

[
      {
        "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"
      }
]

Hier sind meine Modelle:

public class Contacts {

    public List<User> contacts;

}

...

public class User {

    String username;
    String regid;

    @Override
    public String toString(){
        return(username);
    }  

}

meine Schnittstelle:

public interface ContactsInterface {

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

}

meine Erfolgsmethode:

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

Wenn ich es für meine Erfolgsmethode verwende, wird der Fehler ausgegeben

Erwartet BEGIN_OBJECT, war aber BEGIN_ARRAY in Zeile 1 Spalte2

Was ist hier falsch?

Jezer Crespo
quelle

Antworten:

167

Im Moment analysieren Sie die Antwort so, als ob sie wie folgt formatiert wäre:

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

Die Ausnahme sagt Ihnen dies, indem Sie ein Objekt an der Wurzel erwarten, aber die realen Daten tatsächlich ein Array sind. Dies bedeutet, dass Sie den Typ in ein Array ändern müssen.

Der einfachste Weg ist, einfach eine Liste als direkten Typ im Rückruf zu verwenden:

@GET("/users.json")
void contacts(Callback<List<User>> cb);
Jake Wharton
quelle
2
@ AzlanJamal auf die gleiche Weise
Douglas Mesquita
15

dependencies used :

compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'

json-Antworten können eine array responseoder eine object responseoder sogar eine Kombination aus beiden sein. Siehe die folgenden drei Fälle

Case 1 : Parsing a json array response (OPs Fall)

Dieser Fall gilt für diejenigen, json responsesdie von der Form sind[{...} ,{...}]

Z.B

[
  {
    "id": 3,
    "username": "jezer",
    "regid": "oiqwueoiwqueoiwqueoiwq",
    "url": "http:\/\/192.168.63.175:3000\/users\/3.json"
  },
  .
  .
]

Erstellen Sie zuerst eine Modellklasse für dieses Array oder gehen Sie einfach zu jsonschema2pojo und generieren Sie automatisch eine wie unten

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

In diesem Fall sollten Sie eine Liste von Objekten wie die folgende zurückgeben

public interface ContactsInterface {
@GET("/users.json")
Call<List<Contacts>> getContacts();
}

Dann machen Sie den retrofit2Anruf wie folgt aus

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() gibt Ihnen die Liste der Objekte

SIE KÖNNEN AUCH DIE FOLGENDEN ZWEI FÄLLE AUF REFERENZ PRÜFEN

Case 2 : Parsing a json object response

Dieser Fall gilt für json-Antworten, die die Form {..} haben.

Z.B

{
"id": 3,
"username": "jezer",
"regid": "oiqwueoiwqueoiwqueoiwq",
"url": "http:\/\/192.168.63.175:3000\/users\/3.json"
}

Hier haben wir das gleiche objectwie im obigen Beispiel. Die Modellklasse ist also dieselbe, aber wie im obigen Beispiel haben wir kein Array dieser Objekte - nur ein einziges Objekt, und daher müssen wir es nicht als Liste analysieren.

Nehmen Sie also die folgenden Änderungen für ein object response

public interface ContactsInterface {
    @GET("/users.json")
    Call<Contacts> getContacts();
    }

Dann machen Sie den retrofit2Anruf wie folgt aus

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() wird Ihnen das Objekt geben

Sie können auch einen häufigen Fehler überprüfen, während Sie die Antwort des JSON- Objekts analysieren : "erwartetes begin_array, aber begin_object"

Case 3 : Parsing a json array inside json object

Dieser Fall gilt für diejenigen, json responsesdie von der Form sind{"array_name":[{...} ,{...}]}

Z.B

    {
    "contacts": 
         [
            {
             "id": 3,
             "username": "jezer",
             "regid": "oiqwueoiwqueoiwqueoiwq",
             "url": "http:\/\/192.168.63.175:3000\/users\/3.json"
            }
         ]
    }

Sie benötigen hier zwei Modellklassen, da wir zwei Objekte haben (eines außerhalb und eines innerhalb des Arrays). Generieren Sie es wie unten

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

}

Sie können Contacts.javadas oben generierte für die Listenobjekte verwenden (generiert für Fall 1).

Nehmen Sie also die folgenden Änderungen für ein object response

public interface ContactsInterface {
    @GET("/users.json")
    Call<ContactWrapper> getContacts();
    }

Dann machen Sie den retrofit2Anruf wie folgt aus

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

Hier besteht der Unterschied zu Fall 1 darin, dass wir response.body().getContacts()stattdessen verwenden sollten response.body(), um die Liste der Objekte zu erhalten

Einige Referenzen für die oben genannten Fälle:

Fall 1: Analysieren einer JSON-Array-Antwort , Fall 2: Analysieren einer JSON-Objektantwort , gemischt: Analysieren eines JSON-Arrays in einem anderen JSON-Objekt

Navneet Krishna
quelle
hai Ich versuche, Daten im 3. Format anzufordern, aber ich erhalte ständig den gleichen Fehler.
Können
Bitte geben Sie weitere Informationen zu Ihrem Code an und stellen Sie sicher, dass die Schreibweise Ihres Array-Namens in der Modellklasse genau mit der der Antwort
übereinstimmt
8

in Ihrer Schnittstelle ersetzen

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

Durch diesen Code

@GET("/users.json")
void contacts(Callback<List<Contacts>> cb);
Francisco MEza
quelle
1
Dies scheint die gleiche Antwort zu sein, abzüglich der Beschreibung, die Jake Wharton veröffentlicht hat.
Peter
Es ist wahr, ich hatte den gleichen Fehler und bevor ich das Pojo änderte, entschied ich mich, eine ArrayList zu erstellen und das gleiche Ergebnis zu
erzielen
1

Konvertieren Sie es in eine Liste.

Unten ist das Beispiel:

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

Quellcode funktioniert

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();
            }
        });
Keshav Gera
quelle
Ich habe es so versucht, aber ich bekomme response.body () als [] leer .... keine Daten darin, bitte helfen Sie mir
Swapna
0

Verwenden Sie MPV in Ihrem Deserializer

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

JsonArray data  = obj.getAsJsonObject().getAsJsonArray("data");
JoseDuin
quelle
Bitte geben Sie eine Erklärung, warum dies das Problem löst
Sorak
Wenn die Serverantwort ein JSON-Format wie dieses hat {[..]}, kann die Nachrüstung nicht korrekt wiederholt werden. Dem JSON muss ein Präfix hinzugefügt werden, wie Jake Wharton vorschlägt. Der endgültige JSON wäre so {"data":[..]}und das Problem gelöst.
JoseDuin
0

Der Stack hier ist Kotlin, Retrofit2, RxJava und wir migrieren von regulären CallMethoden zu diesem.

Der Dienst, den ich erstellt hatte, warf com.google.gson.JsonSyntaxExceptionund java.lang.IllegalStateExceptionmit Nachricht:

Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column2

Alle Antworten, die ich finden konnte, besagten jedoch, dass dies darauf zurückzuführen war, dass der Dienst keinen Array- Typ enthielt, was ich bereits getan hatte. Mein Kotlin-Service sah folgendermaßen aus:

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

Das Problem war das Problem Result, das ich eingegeben hatte, als ich eine frühere CallLösung verwendete.

Durch Entfernen wurde das Problem behoben. Ich musste auch die Signatur der beiden Fehlerbehandlungsmethoden an meiner Anrufstelle für den Dienst ändern:

/// WORKING SERVICE
interface InventoryService {

    @GET("getInventoryData/{storeId}")
    fun getInventoryData(@Path("storeId") storeId: String,
                         @Query("startDate") startDate: String,
                         @Query("endDate") endDate: String) :
            Single<List<InventoryData>>
}

Und der Call-Site-Fragmentcode, der den Dienst verwendet:

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

Beachten Sie, dass der obige Code ein wenig geändert wurde. Insbesondere habe ich ein Retrofit2 verwendet ConverterFactory, um die Übergabe der Daten als OffsetDateTime-Objekte anstelle von Zeichenfolgen zu ermöglichen.

Sez
quelle