GSON lanzando "Se esperaba BEGIN_OBJECT pero fue BEGIN_ARRAY"?

295

Estoy tratando de analizar una cadena JSON como esta

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

en una lista de objetos.

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

Aquí hay una clase de objeto que estoy usando.

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

        }

Pero me arroja con

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

¿Alguna idea de cómo debería solucionarlo?

Roger Travis
fuente
12
@Soni: eso es incorrecto. Si va a jsonlint.org y copia / pega su JSON, verá que es válido.
Brian Roach
@Soni - no, eliminó "[" y "]", pero sigue siendo el mismo. Supongo que podría ser más porque la cadena que tengo contiene varios objetos, no solo uno.
Roger Travis
¿Qué jstringaspecto tiene al que aludió en su código?
IgorGanapolsky
Observo un pensamiento, cuando la respuesta vuelve en la matriz y luego trato de incluir en la Lista, es resolver mi problema.
iamkdblue

Respuestas:

331

El problema es que estás diciendo Gsonque tienes un objeto de tu tipo. Usted no Tienes una gran variedad de objetos de tu tipo. No puedes simplemente tratar de emitir el resultado así y esperar que funcione mágicamente;)

La Guía del usuario de GsonExplica cómo lidiar con esto:

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

Esto funcionará:

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

Pero esto es mejor:

Type collectionType = new TypeToken<Collection<ChannelSearchEnum>>(){}.getType();
Collection<ChannelSearchEnum> enums = gson.fromJson(yourJson, collectionType);
Brian Roach
fuente
probablemente de hecho. como una matriz de objetos, el tipo se conserva en tiempo de ejecución para que gson sepa qué buscar. buena idea.
njzk2
3
+1 para el TypoToken<Collection<Something>>- no use matrices cuando puede tener Colección (subclases) y / o Iterables.
Philipp Reichart
¿Crees que es el método correcto para analizar obj / array seleccionado? ayuda stackoverflow.com/questions/18140830/…
LOG_TAG
1
¿Qué pasa si queremos hacerlo con una cuerda; Por ejemplo, ¿puedo escribir algo como String [] t = gson.fromJson (myJson, String []. Class)
Sahin Yanlık
44
Sintiendo que esta respuesta está inacabada !!
EngineSense
45

El problema es que está pidiendo un objeto de tipo, ChannelSearchEnumpero lo que realmente tiene es un objeto de tipo List<ChannelSearchEnum>.

Puede lograr esto con:

Type collectionType = new TypeToken<List<ChannelSearchEnum>>(){}.getType();
List<ChannelSearchEnum> lcs = (List<ChannelSearchEnum>) new Gson()
               .fromJson( jstring , collectionType);
Guillaume Polet
fuente
1
que tipo de Typees eso que importar
smatthewenglish
44
@ S.Matthew_English muy probablementejava.lang.reflect.Type
Guillaume Polet
36

En mi caso, la cadena 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"}]

e imprimo "category" y "url_title" en 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();
}

Adaptador de datos

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

y finalmente 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
fuente
55
mejor respuesta para ese tipo de problema
Nicky Manali
44
Esto responde perfectamente a la pregunta, especialmente para los usuarios de modificaciones. Para cualquiera que busque claridad, la parte que más necesita es Call <List <Datum>> getJSON ();
Carlos Anyona
13

La alternativa podría ser

para que su respuesta se vea como

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

en vez 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"
   }
]

CÓDIGO

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

Después de esto, será cualquier otro GSON Parsing

DeltaCap019
fuente
¿Qué pasa si no puedo cambiar mi formato json? Estoy usando la solicitud gson de volley para establecer mi clase de modelo. ¿Cómo hacerlo? Gracias
Kaveesh Kanwal
@KaveeshKanwal probar otras soluciones proporcionadas en este hilo, aparte de esto, no tengo idea
DeltaCap019
8

según la guía del usuario de GSON , no puede.

Limitaciones de colecciones

Puede serializar la colección de objetos arbitrarios pero no puede deserializarla. Porque no hay forma de que el usuario indique el tipo del objeto resultante

njzk2
fuente
77
No tiene una colección de objetos arbitrarios, tiene una colección de un tipo específico de objeto con el que se Gsonocupará felizmente
Brian Roach
en realidad, comencé escribiendo una respuesta con TypeToken tal como lo hiciste, pero como el tipo genérico no está incrustado en tiempo de ejecución, no vi cómo podría funcionar eso. (Aunque no lo he probado).
njzk2
3

Esto parece una lista de matriz Json, por lo tanto, es mejor usarlo ArrayListpara manejar los datos. En su punto final de la API, agregue una lista de matriz como esta

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

Debe informar a Gson el tipo adicional de su respuesta como se muestra a continuación

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
fuente
1

No estoy seguro de si esta es la mejor manera de usar GSON, pero funciona para mí. Puede usar algunos como este en 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());
    }
}

Es suficiente con cuerdas, pero si usted tendría dobles o int, se puede poner getDoubleo getInttambién.

El método de IOHelperclase es el siguiente (aquí, la ruta se guarda en el almacenamiento interno):

 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 desea más información sobre esto, puede ver este video , donde obtengo el código de readJson(); y este hilo donde obtengo el código de getData().

PJ Alzab
fuente
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
fuente
No voté a favor ni en contra, nada, solo edité.
Shredator