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?
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 response
oder eineobject response
oder sogar eine Kombination aus beiden sein. Siehe die folgenden drei FälleCase 1 : Parsing a json array response
(OPs Fall)Dieser Fall gilt für diejenigen,
json responses
die 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
retrofit2
Anruf wie folgt ausRetrofit 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 ObjekteSIE 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
object
wie 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
retrofit2
Anruf wie folgt ausRetrofit 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 gebenSie 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 responses
die 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.java
das 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
retrofit2
Anruf wie folgt ausRetrofit 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 solltenresponse.body()
, um die Liste der Objekte zu erhaltenEinige 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
quelle
in Ihrer Schnittstelle ersetzen
@GET("/users.json") void contacts(Callback<Contacts> cb);
Durch diesen Code
@GET("/users.json") void contacts(Callback<List<Contacts>> cb);
quelle
Konvertieren Sie es in eine Liste.
Unten ist das Beispiel:
quelle
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(); } });
quelle
Verwenden Sie MPV in Ihrem Deserializer
JsonObject obj = new JsonObject(); obj.add("data", json); JsonArray data = obj.getAsJsonObject().getAsJsonArray("data");
quelle
{[..]}
, 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.Der Stack hier ist Kotlin, Retrofit2, RxJava und wir migrieren von regulären
Call
Methoden zu diesem.Der Dienst, den ich erstellt hatte, warf
com.google.gson.JsonSyntaxException
undjava.lang.IllegalStateException
mit 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ühereCall
Lö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.quelle