it-swarm.com.de

Retrofit Erwartete BEGIN_OBJECT war aber BEGIN_ARRAY

Ich bin relativ 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 

Erwartete BEGIN_OBJECT, war jedoch in Zeile 1 Spalte2 BEGIN_ARRAY

Was ist hier falsch?

62
Jezer Crespo

Jetzt analysieren Sie die Antwort so, als wäre sie so formatiert:

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

Die Ausnahme sagt Ihnen, dass Sie ein Objekt im Stammverzeichnis erwarten, die realen Daten jedoch tatsächlich ein Array sind. Das bedeutet, dass Sie den Typ in ein Array ändern müssen.

Am einfachsten ist es, eine Liste als direkten Typ im Rückruf zu verwenden:

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

in Ihrer Schnittstelle ersetzen 

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

Mit diesem Code

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

dependencies used :

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

json-Antworten können ein array response oder ein object response oder auch eine Kombination aus beiden sein. Siehe die folgenden drei Fälle

Case 1 : Parsing a json array response (Fall des OP)

Dieser Fall trifft auf diejenigen json responses zu, die die Form [{...} ,{...}] haben.

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 einfach goto jsonschema2pojo und generieren Sie eine wie unten beschrieben

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 rufen Sie den retrofit2-Aufruf wie folgt auf

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 VERWEIS AUFNEHMEN

Case 2 : Parsing a json object response

Dieser Fall trifft auf json-Antworten zu, 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 die gleiche object wie im obigen Beispiel. Die Modellklasse ist also dieselbe, aber wie im obigen Beispiel haben wir kein Array dieser Objekte - nur ein einzelnes Objekt, und wir müssen es nicht als Liste analysieren.

Nehmen Sie also die folgenden Änderungen für einen object response vor.

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

Dann rufen Sie den retrofit2-Aufruf wie folgt auf

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

Sie können auch einen allgemeinen Fehler überprüfen, während Sie die Antwort des Json-Objekts analysieren: "erwartet begin_array, aber war begin_object"

Case 3 : Parsing a json array inside json object

Dieser Fall trifft auf diejenigen json responses zu, die die Form {"array_name":[{...} ,{...}]} haben.

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 folgt

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.Java verwenden, das oben für die Listenobjekte generiert wurde (generiert für Fall 1).

Nehmen Sie also die folgenden Änderungen für einen object response vor.

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

Dann rufen Sie den retrofit2-Aufruf wie folgt auf

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

Der Unterschied zu Fall 1 besteht darin, dass wir response.body().getContacts() anstelle von response.body() verwenden sollten, um die Liste der Objekte abzurufen

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

case 1: Parsing einer Antwort eines Json-Arrays , Case 2: Parsen einer Antwort eines Json-Objekts , mixed: Parsen eines Json-Arrays in einem anderen Json-Objekt

2
Navneet Krishna

Konvertieren Sie es in eine Liste.

Unten ist das Beispiel:

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

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

Verwenden SieMPVin Ihrem Deserializer

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

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

Der Stack hier ist Kotlin, Retrofit2, RxJava und wir migrieren zu jenen aus regulären Call Methoden.

Der Dienst, den ich erstellt hatte, war com.google.gson.JsonSyntaxException und Java.lang.IllegalStateException mit der Meldung zu werfen:

Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column2

Aber alle Antworten, die ich finden konnte, besagten, dass dies darauf zurückzuführen war, dass der Dienst nicht mit einem Array - Typ versehen war, was ich bereits getan habe. Mein Kotlin-Service sah so 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 der Result, den ich eingegeben hatte, als ich eine frühere Call-basierte Lösung verwendete.

Durch das Entfernen wurde das Problem behoben. Ich musste auch die Signatur der beiden Fehlerbehandlungsmethoden an meiner Call-Site für den Service ä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 Retrofit2 ConverterFactory verwendet, um zu ermöglichen, dass die Daten als OffsetDateTime-Objekte anstelle von Zeichenfolgen übergeben werden.

0
Sez