web-dev-qa-db-fra.com

Rénovation attendue BEGIN_OBJECT mais BEGIN_ARRAY

Je suis assez nouveau dans l'analyse JSON, j'utilise la bibliothèque de Retrofit de Square et j'ai rencontré ce problème. 

J'essaie d'analyser cette réponse 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"
      }
]

Voici mes modèles:

public class Contacts {

    public List<User> contacts;

}

...

public class User {

    String username;
    String regid;

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

}

mon interface:

public interface ContactsInterface {

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

}

ma méthode de réussite:

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

Quand je l'utilise sur ma méthode de réussite, l'erreur est renvoyée. 

BEGIN_OBJECT attendu mais BEGIN_ARRAY à la ligne 1, colonne2

Qu'est-ce qui ne va pas ici?

62
Jezer Crespo

En ce moment, vous analysez la réponse comme si elle était formatée comme ceci:

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

L'exception vous dit ceci en ce sens que vous attendez un objet à la racine mais que les données réelles sont en réalité un tableau. Cela signifie que vous devez changer le type pour qu'il s'agisse d'un tableau.

Le moyen le plus simple consiste simplement à utiliser une liste comme type direct dans le rappel:

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

dans votre interface remplacer 

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

Par ce 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'

les réponses json peuvent être un array response ou un object response ou même une combinaison des deux. Voir les trois cas suivants

Case 1 : Parsing a json array response (cas de l'OP)

Ce cas s'applique à ceux json responses qui sont de la forme [{...} ,{...}]

Par exemple. 

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

Commencez par créer une classe de modèle pour ce tableau ou bien passez à goto jsonschema2pojo et générez-en automatiquement une comme ci-dessous

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

Dans ce cas, vous devriez renvoyer une liste d'objets comme suit:

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

Puis appelez retrofit2 comme suit

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() vous donnera la liste des objets

VOUS POUVEZ ÉGALEMENT CONSULTER LES DEUX CAS SUIVANTS POUR RÉFÉRENCE

Case 2 : Parsing a json object response

Ce cas s’applique aux réponses JSON qui se présentent sous la forme {..}

Par exemple.

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

Ici, nous avons la même variable object que ci-dessus. Donc, la classe de modèle sera la même, mais comme dans l'exemple ci-dessus, nous n'avons pas un tableau de ces objets - juste un seul objet et, par conséquent, nous n'avons pas besoin de l'analyser sous forme de liste.

Alors apportez les modifications suivantes pour un object response

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

Puis appelez retrofit2 comme suit

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() vous donnera l'objet

Vous pouvez également vérifier une erreur commune lors de l'analyse de la réponse à un objet Json: "attendue begin_array mais était begin_object"

Case 3 : Parsing a json array inside json object

Ce cas s'applique à ceux json responses qui sont de la forme {"array_name":[{...} ,{...}]}

Par exemple.

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

Vous aurez besoin de deux classes de modèle ici car nous avons deux objets (un à l'extérieur et un à l'intérieur du tableau). Générez-le comme ci-dessous

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

}

Vous pouvez utiliser Contacts.Java généré ci-dessus pour les objets de liste (généré pour le cas 1)

Alors apportez les modifications suivantes pour un object response

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

Puis appelez retrofit2 comme suit

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

Ici, la différence avec le cas 1 est que nous devrions utiliser response.body().getContacts() au lieu de response.body() pour obtenir la liste des objets.

Quelques références pour les cas ci-dessus:

case 1: Analyse d'une réponse de tableau JSON , cas 2: Analyse d'une réponse d'objet Json , mixed: Analyse de tableau Json dans un autre objet JSON

2
Navneet Krishna

Convertissez-le en une liste.

Voici l'exemple:

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

Code source fonctionnant

https://drive.google.com/open?id=0BzBKpZ4nzNzUVFRnVVzz0JabUU

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

En utilisantMPV, dans votre désérialiseur, mettez ceci

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

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

La pile ici est Kotlin, Retrofit2, RxJava et nous migrons vers cette méthode hors des méthodes Call normales.

Le service que j'avais créé jetait com.google.gson.JsonSyntaxException et Java.lang.IllegalStateException avec le message suivant:

Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column2

Mais toutes les réponses que j'ai pu trouver indiquaient que cela était dû à l'absence de type array dans le service, ce que j'avais déjà fait. Mon service Kotlin ressemblait à ceci:

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

Le problème était la Result dedans, que j'avais mise lorsque j'utilisais une solution antérieure basée sur Call.

Le supprimer a résolu le problème. J'ai également dû modifier la signature des deux méthodes de traitement des erreurs sur mon site d'appels pour le service:

/// WORKING SERVICE
interface InventoryService {

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

Et le code de fragment de site d’appel qui utilise le service:

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

Notez que le code ci-dessus a été légèrement modifié. En particulier, j'ai utilisé Retrofit2 ConverterFactory pour permettre aux dates d'être transmises sous forme d'objets OffsetDateTime au lieu de chaînes.

0
Sez