GSON lançant "BEGIN_OBJECT attendu mais BEGIN_ARRAY"?

295

J'essaie d'analyser une chaîne JSON comme celle-ci

[
   {
      "updated_at":"2012-03-02 21:06:01",
      "fetched_at":"2012-03-02 21:28:37.728840",
      "description":null,
      "language":null,
      "title":"JOHN",
      "url":"http://rus.JOHN.JOHN/rss.php",
      "icon_url":null,
      "logo_url":null,
      "id":"4f4791da203d0c2d76000035",
      "modified":"2012-03-02 23:28:58.840076"
   },
   {
      "updated_at":"2012-03-02 14:07:44",
      "fetched_at":"2012-03-02 21:28:37.033108",
      "description":null,
      "language":null,
      "title":"PETER",
      "url":"http://PETER.PETER.lv/rss.php",
      "icon_url":null,
      "logo_url":null,
      "id":"4f476f61203d0c2d89000253",
      "modified":"2012-03-02 23:28:57.928001"
   }
]

dans une liste d'objets.

List<ChannelSearchEnum> lcs = (List<ChannelSearchEnum>) new Gson().fromJson( jstring , ChannelSearchEnum.class);

Voici une classe d'objets que j'utilise.

import com.google.gson.annotations.SerializedName;

public class ChannelSearchEnum {



@SerializedName("updated_at")
private String updated_at;

@SerializedName("fetched_at")
private String fetched_at;

@SerializedName("description")
private String description;

@SerializedName("language")
private String language;

@SerializedName("title")
private String title;

@SerializedName("url")
private String url;

@SerializedName("icon_url")
private String icon_url;

@SerializedName("logo_url")
private String logo_url;

@SerializedName("id")
private String id;

@SerializedName("modified")
private String modified;

public final String get_Updated_at() {
    return this.updated_at;
}

public final String get_Fetched_at() {
    return this.fetched_at;
}

public final String get_Description() {
    return this.description;
}

public final String get_Language() {
    return this.language;
}

public final String get_Title() {
    return this.title;
}

public final String get_Url() {
    return this.url;
}

public final String get_Icon_url() {
    return this.icon_url;
}

public final String get_Logo_url() {
    return this.logo_url;
}

public final String get_Id() {
    return this.id;
}

public final String get_Modified() {
    return this.modified;
}

        }

Mais ça me jette avec

com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2

Des idées comment dois-je le réparer?

Roger Travis
la source
12
@Soni - c'est incorrect. Si vous allez sur jsonlint.org et copiez / collez son JSON, vous verrez qu'il est valide.
Brian Roach
@Soni - nope, supprimé "[" et "]", mais toujours le même. suppose que ce pourrait être plus parce que la chaîne que j'ai contient plusieurs objets, pas un seul.
Roger Travis
À quoi jstringressemble votre allusion dans votre code?
IgorGanapolsky
J'observe une réflexion, lorsque le retour de la réponse dans le tableau, puis essayez de prendre dans la liste, cela résout mon problème.
iamkdblue

Réponses:

331

Le problème est que vous dites Gson vous avez un objet de votre type. Non. Vous disposez d'un tableau d'objets de votre type. Vous ne pouvez pas simplement essayer de lancer le résultat comme ça et vous attendre à ce qu'il fonctionne comme par magie;)

Le Guide de l'utilisateur pour Gsonexplique comment gérer cela:

https://github.com/google/gson/blob/master/UserGuide.md

Cela fonctionnera:

ChannelSearchEnum[] enums = gson.fromJson(yourJson, ChannelSearchEnum[].class);

Mais c'est mieux:

Type collectionType = new TypeToken<Collection<ChannelSearchEnum>>(){}.getType();
Collection<ChannelSearchEnum> enums = gson.fromJson(yourJson, collectionType);
Brian Roach
la source
probablement en effet. en tant que tableau d'objets, le type est conservé au moment de l'exécution afin que gson sache quoi rechercher. bonne idée.
njzk2
3
+1 pour le TypoToken<Collection<Something>>- n'utilisez pas de tableaux lorsque vous pouvez avoir Collection (sous-classes) et / ou Iterables.
Philipp Reichart
Pensez-vous que c'est la bonne méthode pour analyser l'obj / tableau sélectionné? help stackoverflow.com/questions/18140830/…
LOG_TAG
1
Et si nous voulons le faire avec de la ficelle; Par exemple, puis-je écrire quelque chose comme String [] t = gson.fromJson (myJson, String []. Class)
Sahin Yanlık
4
Sentant que cette réponse est inachevée !!
EngineSense
45

Le problème est que vous demandez un objet de type ChannelSearchEnummais ce que vous avez réellement est un objet de type List<ChannelSearchEnum>.

Vous pouvez y parvenir avec:

Type collectionType = new TypeToken<List<ChannelSearchEnum>>(){}.getType();
List<ChannelSearchEnum> lcs = (List<ChannelSearchEnum>) new Gson()
               .fromJson( jstring , collectionType);
Guillaume Polet
la source
1
c'est quoi Typeça? quoi importer?
smatthewenglish
4
@ S.Matthew_English très probablementjava.lang.reflect.Type
Guillaume Polet
36

Dans mon cas, la chaîne JSON:

[{"category":"College Affordability",
  "uid":"150151",
  "body":"Ended more than $60 billion in wasteful subsidies for big banks and used the savings to put the cost of college within reach for more families.",
  "url":"http:\/\/www.whitehouse.gov\/economy\/middle-class\/helping middle-class-families-pay-for-college",
  "url_title":"ending subsidies for student loan lenders",
  "type":"Progress",
  "path":"node\/150385"}]

et j'imprime "category" et "url_title" dans recycleview

Datum.class

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

public class Datum {
@SerializedName("category")
@Expose
private String category;
@SerializedName("uid")
@Expose
private String uid;
@SerializedName("url_title")
@Expose
private String urlTitle;

/**
 * @return The category
 */
public String getCategory() {
    return category;
}

/**
 * @param category The category
 */
public void setCategory(String category) {
    this.category = category;
}

/**
 * @return The uid
 */
public String getUid() {
    return uid;
}

/**
 * @param uid The uid
 */
public void setUid(String uid) {
    this.uid = uid;
}

/**
 * @return The urlTitle
 */
public String getUrlTitle() {
    return urlTitle;
}

/**
 * @param urlTitle The url_title
 */
public void setUrlTitle(String urlTitle) {
    this.urlTitle = urlTitle;
}

}

RequestInterface

import java.util.List;

import retrofit2.Call;
import retrofit2.http.GET;

/**
 * Created by Shweta.Chauhan on 13/07/16.
 */

public interface RequestInterface {

   @GET("facts/json/progress/all")
   Call<List<Datum>> getJSON();
}

DataAdapter

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Shweta.Chauhan on 13/07/16.
 */

public class DataAdapter extends RecyclerView.Adapter<DataAdapter.MyViewHolder>{

private Context context;
private List<Datum> dataList;

public DataAdapter(Context context, List<Datum> dataList) {
    this.context = context;
    this.dataList = dataList;
}

@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.data,parent,false);
    return new MyViewHolder(view);
}

@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
    holder.categoryTV.setText(dataList.get(position).getCategory());
    holder.urltitleTV.setText(dataList.get(position).getUrlTitle());

}

@Override
public int getItemCount() {
    return dataList.size();
}

public class MyViewHolder extends RecyclerView.ViewHolder{

    public TextView categoryTV, urltitleTV;

    public MyViewHolder(View itemView) {
        super(itemView);
        categoryTV = (TextView) itemView.findViewById(R.id.txt_category);
        urltitleTV = (TextView)     itemView.findViewById(R.id.txt_urltitle);
    }
}
}

et enfin MainActivity.java

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class MainActivity extends AppCompatActivity {

private RecyclerView recyclerView;
private DataAdapter dataAdapter;
private List<Datum> dataArrayList;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    initViews();
}

private void initViews(){
    recyclerView=(RecyclerView) findViewById(R.id.recycler_view);
    recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
    loadJSON();
}

private void loadJSON(){
    dataArrayList = new ArrayList<>();
    Retrofit retrofit=new Retrofit.Builder().baseUrl("https://www.whitehouse.gov/").addConverterFactory(GsonConverterFactory.create()).build();
    RequestInterface requestInterface=retrofit.create(RequestInterface.class);
    Call<List<Datum>> call= requestInterface.getJSON();
    call.enqueue(new Callback<List<Datum>>() {
        @Override
        public void onResponse(Call<List<Datum>> call, Response<List<Datum>> response) {
            dataArrayList = response.body();
            dataAdapter=new DataAdapter(getApplicationContext(),dataArrayList);
            recyclerView.setAdapter(dataAdapter);
        }

        @Override
        public void onFailure(Call<List<Datum>> call, Throwable t) {
            Log.e("Error",t.getMessage());
        }
    });
}
}
Shweta Chauhan
la source
5
meilleure réponse pour ce type de problème
Nicky Manali
4
Cela répond parfaitement à la question en particulier pour les utilisateurs de retrofit. Pour tous ceux qui recherchent la clarté, la partie dont vous avez le plus besoin est Call <List <Datum>> getJSON ();
Carlos Anyona
13

Une alternative pourrait être

pour faire ressembler votre réponse

myCustom_JSONResponse

{"master":[
   {
      "updated_at":"2012-03-02 21:06:01",
      "fetched_at":"2012-03-02 21:28:37.728840",
      "description":null,
      "language":null,
      "title":"JOHN",
      "url":"http://rus.JOHN.JOHN/rss.php",
      "icon_url":null,
      "logo_url":null,
      "id":"4f4791da203d0c2d76000035",
      "modified":"2012-03-02 23:28:58.840076"
   },
   {
      "updated_at":"2012-03-02 14:07:44",
      "fetched_at":"2012-03-02 21:28:37.033108",
      "description":null,
      "language":null,
      "title":"PETER",
      "url":"http://PETER.PETER.lv/rss.php",
      "icon_url":null,
      "logo_url":null,
      "id":"4f476f61203d0c2d89000253",
      "modified":"2012-03-02 23:28:57.928001"
   }
]
}

au lieu de

server_JSONResponse

[
   {
      "updated_at":"2012-03-02 21:06:01",
      "fetched_at":"2012-03-02 21:28:37.728840",
      "description":null,
      "language":null,
      "title":"JOHN",
      "url":"http://rus.JOHN.JOHN/rss.php",
      "icon_url":null,
      "logo_url":null,
      "id":"4f4791da203d0c2d76000035",
      "modified":"2012-03-02 23:28:58.840076"
   },
   {
      "updated_at":"2012-03-02 14:07:44",
      "fetched_at":"2012-03-02 21:28:37.033108",
      "description":null,
      "language":null,
      "title":"PETER",
      "url":"http://PETER.PETER.lv/rss.php",
      "icon_url":null,
      "logo_url":null,
      "id":"4f476f61203d0c2d89000253",
      "modified":"2012-03-02 23:28:57.928001"
   }
]

CODE

  String server_JSONResponse =.... // the string in which you are getting your JSON Response after hitting URL
String myCustom_JSONResponse="";// in which we will keep our response after adding object element to it
     MyClass apiResponse = new MyClass();

     myCustom_JSONResponse="{\"master\":"+server_JSONResponse+"}";



    apiResponse = gson.fromJson(myCustom_JSONResponse, MyClass .class);

Après cela, ce sera tout autre GSON Parsing

DeltaCap019
la source
que faire si je ne peux pas changer mon format json? J'utilise la demande gson de volley pour définir ma classe de modèle. Comment faire? Merci
Kaveesh Kanwal
@KaveeshKanwal essayez d'autres solutions fournies dans ce fil, à part cela, je n'en ai aucune idée
DeltaCap019
8

selon le guide de l'utilisateur GSON , vous ne pouvez pas.

Limitations des collections

Peut sérialiser la collection d'objets arbitraires mais ne peut pas en désérialiser. Parce qu'il n'y a aucun moyen pour l'utilisateur d'indiquer le type de l'objet résultant

njzk2
la source
7
Il n'a pas de collection d'objets arbitraires, il a une collection d' un type spécifique d'objet avec lequel il Gsonsera heureux de faire face
Brian Roach
en fait, j'ai commencé par écrire une réponse avec le TypeToken comme vous l'avez fait, mais comme le type générique n'est pas intégré au moment de l'exécution, je n'ai pas vu comment cela pourrait éventuellement fonctionner. (même si je ne l'ai pas testé).
njzk2
3

Cela ressemble à une liste de tableaux Json, il est donc préférable de l'utiliser ArrayListpour gérer les données. Dans votre point de terminaison api, ajoutez une liste de tableaux comme celle-ci

 @GET("places/")
Call<ArrayList<Place>> getNearbyPlaces(@Query("latitude") String latitude, @Query("longitude") String longitude);
Nelson Katale
la source
1

Vous devez informer Gson du type supplémentaire de votre réponse comme ci-dessous

import com.google.common.reflect.TypeToken;
import java.lang.reflect.Type;


Type collectionType = new TypeToken<List<UserSite>>(){}.getType();
List<UserSite> userSites  = gson.fromJson( response.getBody() , collectionType);
Pravin
la source
1

Je ne sais pas si c'est la meilleure façon d'utiliser GSON, mais ça marche pour moi. Vous pouvez en utiliser comme ceci sur MainActivity:

 public void readJson() {
    dataArrayList = new ArrayList<>();
    String json = "[\n" + IOHelper.getData(this) + "\n]\n";
    Log.d(TAG, json);
    try{
        JSONArray channelSearchEnums = new JSONArray(json);

        for(int i=0; i< channelSearchEnums.length(); i++)
        {
            JSONObject enum = channelSearchEnums.getJSONObject(i);
            ChannelSearchEnum channel = new ChannelSearchEnum(
                   enum.getString("updated_at"), enum.getString("fetched_at"),
                   enum.getString("description"), enum.getString("language"),
                   enum.getString("title"), enum.getString("url"),
                   enum.getString("icon_url"), enum.getString("logo_url"),
                   enum.getString("id"), enum.getString("modified"))         

                   dataArrayList.add(channel);
        }

         //The code and place you want to show your data            

    }catch (Exception e)
    {
        Log.d(TAG, e.getLocalizedMessage());
    }
}

Vous n'avez que des cordes, mais si vous aviez des doubles ou des int, vous pourriez mettre getDoubleou getIntaussi.

La méthode de IOHelperclasse est la suivante (Ici, le chemin est sauvegardé sur le stockage interne):

 public static String getData(Context context) {
    try {
        File f = new File(context.getFilesDir().getPath() + "/" + fileName);
        //check whether file exists
        FileInputStream is = new FileInputStream(f);
        int size = is.available();
        byte[] buffer = new byte[size];
        is.read(buffer);
        is.close();
        return new String(buffer);
    } catch (IOException e) {
        Log.e("TAG", "Error in Reading: " + e.getLocalizedMessage());
        return null;
    }
}

Si vous voulez plus d'informations à ce sujet, vous pouvez voir cette vidéo , où je reçois le code de readJson(); et ce fil où je reçois le code de getData().

PJ Alzab
la source
0

Kotlin:

var list=ArrayList<Your class name>()
val listresult: Array<YOUR CLASS NAME> = Gson().fromJson(
                YOUR JSON RESPONSE IN STRING,
                Array<Your class name>:: class.java)

list.addAll(listresult)
Abhay kumar bhumihar
la source
Je n'ai rien voté ou baissé, seulement édité.
Shredator