336

I'm trying to parse a JSON string like this one

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

into a list of objects.

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

Here's an object class I'm using.

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

        }

But it throws me with

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

Any ideas how should I fix it?

Chris
  • 4,593
  • 1
  • 33
  • 37
Roger Travis
  • 8,402
  • 18
  • 67
  • 94
  • 13
    @Soni - that is incorrect. If you go to jsonlint.org and copy/paste his JSON you will see that it is valid. – Brian Roach Mar 07 '12 at 09:39
  • @Soni - nope, removed "[" and "]", but still the same. guess it might be more because the string I have contains multiple objects, not just one. – Roger Travis Mar 07 '12 at 09:41
  • What does your `jstring` look like that you alluded to in your code? – IgorGanapolsky Jan 18 '16 at 23:49
  • i observe one think , when response return in array then try to take in List ,it's solve my problem . – iamkdblue Mar 12 '18 at 12:28
  • Also, please check if your API Interface has the correct return type. This happens when you are expecting a List but have an Object defined as the return type. – Prince Oct 06 '20 at 10:36

11 Answers11

384

The problem is you're telling Gson you have an object of your type. You don't. You have an array of objects of your type. You can't just try and cast the result like that and expect it to magically work ;)

The User guide for Gson Explains how to deal with this:

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

This will work:

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

But this is better:

Type collectionType = new TypeToken<Collection<ChannelSearchEnum>>(){}.getType();
Collection<ChannelSearchEnum> enums = gson.fromJson(yourJson, collectionType);
Chris
  • 4,593
  • 1
  • 33
  • 37
Brian Roach
  • 76,169
  • 12
  • 136
  • 161
56

The problem is that you are asking for an object of type ChannelSearchEnum but what you actually have is an object of type List<ChannelSearchEnum>.

You can achieve this with:

Type collectionType = new TypeToken<List<ChannelSearchEnum>>(){}.getType();
List<ChannelSearchEnum> lcs = (List<ChannelSearchEnum>) new Gson()
               .fromJson( jstring , collectionType);
Chris
  • 4,593
  • 1
  • 33
  • 37
Guillaume Polet
  • 47,259
  • 4
  • 83
  • 117
50

In my case JSON string:

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

and I print "category" and "url_title" in 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);
    }
}
}

and finally 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());
        }
    });
}
}
Deepam Goel
  • 135
  • 1
  • 4
  • 8
Shweta Chauhan
  • 6,739
  • 6
  • 37
  • 57
17

Alternative could be

to make your response look like

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

instead of

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

After this it will be just any other GSON Parsing

DeltaCap019
  • 6,532
  • 3
  • 48
  • 70
10

This looks like a Json array list.Therefore its best to use ArrayList to handle the data. In your api end point add array list like this

 @GET("places/")
Call<ArrayList<Place>> getNearbyPlaces(@Query("latitude") String latitude, @Query("longitude") String longitude);
Nelson Katale
  • 1,309
  • 15
  • 21
8

according to GSON User guide, you cannot.

Collections Limitations

Can serialize collection of arbitrary objects but can not deserialize from it. Because there is no way for the user to indicate the type of the resulting object

Community
  • 1
  • 1
njzk2
  • 38,969
  • 7
  • 69
  • 107
  • 8
    He doesn't have an collection of arbitrary objects, he has a collection of *one specific* type of object which `Gson` will happily deal with – Brian Roach Mar 07 '12 at 09:49
  • actually, i started by writing an answer with the TypeToken just as you did, but since the generic type is not embedded at runtime, i didn't see how that could possibly work. (though i haven't tested it). – njzk2 Mar 07 '12 at 13:36
7

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)
Shredator
  • 940
  • 3
  • 17
  • 32
4

You need to let Gson know additional type of your response as below

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 Bansal
  • 4,315
  • 1
  • 28
  • 19
2

I am not sure if this is the best way to use GSON, but works for me. You can use some like this on the 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());
    }
}

You only have strings, but if you would have doubles or int, you could put getDouble or getInt too.

The method of IOHelper class is the next (Here, the path is save on the internal Storage):

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

If you want more information about this, you can see this video, where I get the code of readJson(); and this thread where I get the code of getData().

0
public ChannelSearchEnum[] getChannelSearchEnum(Response response) {
        return response.as(ChannelSearchEnum[].class, ObjectMapperType.GSON);
}

Above will solve and passing response will returned mapped object array of the class

Tested
  • 11
  • 2
0

Solution

  1. I am using volley library. I parse response automatic in volley using GSON

    [
            {
                "name": "Naruto: Shippuuden",
                "description": "It has been two and a half years since Naruto Uzumaki left Konohagakure, the Hidden Leaf Village, for intense training following events which fueled his desire to be stronger. Now Akatsuki, the mysterious organization of elite rogue ninja, is closing in on their grand plan which may threaten the safety of the entire shinobi world.",
                "Rating": "8.16",
                "episode": 500,
                "categorie":"Animation | Drama | Adventure",
                "studio":"Studio Pierrot",
                "img": "https://myanimelist.cdn-dena.com/images/anime/5/17407.jpg"
            },
            {
                "name": "One Piece",
                "description": "Gol D. Roger was known as the 'Pirate King',the strongest and most infamous being to have sailed the Grand Line. The capture and death of Roger by the World Government brought a change throughout the world. His last words before his death revealed the existence of the greatest treasure in the world, One Piece. It was this revelation that brought about the Grand Age of Pirates, men who dreamed of finding One Piece—which promises an unlimited amount of riches and fame—and quite possibly the pinnacle of glory and the title of the Pirate King.",
                "Rating": "8.54",
                "episode": 700,
                 "categorie":"Animation | Drama | Adventure",
                "studio":"Toei Animation",
                "img": "https://myanimelist.cdn-dena.com/images/anime/6/73245.jpg"
            }
        ]

2.This my model


    public class DataResponse implements Serializable {
    
        @SerializedName("studio")
        private String studio;
    
        @SerializedName("img")
        private String img;
    
        @SerializedName("categorie")
        private String categorie;
    
        @SerializedName("Rating")
        private String rating;
    
        @SerializedName("name")
        private String name;
    
        @SerializedName("description")
        private String description;
    
        @SerializedName("episode")
        private int episode;
    
        public void setStudio(String studio){
            this.studio = studio;
        }
    
        public String getStudio(){
            return studio;
        }
    
        public void setImg(String img){
            this.img = img;
        }
    
        public String getImg(){
            return img;
        }
    
        public void setCategorie(String categorie){
            this.categorie = categorie;
        }
    
        public String getCategorie(){
            return categorie;
        }
    
        public void setRating(String rating){
            this.rating = rating;
        }
    
        public String getRating(){
            return rating;
        }
    
        public void setName(String name){
            this.name = name;
        }
    
        public String getName(){
            return name;
        }
    
        public void setDescription(String description){
            this.description = description;
        }
    
        public String getDescription(){
            return description;
        }
    
        public void setEpisode(int episode){
            this.episode = episode;
        }
    
        public int getEpisode(){
            return episode;
        }
    
        @Override
        public String toString(){
            return 
                "Response{" + 
                "studio = '" + studio + '\'' + 
                ",img = '" + img + '\'' + 
                ",categorie = '" + categorie + '\'' + 
                ",rating = '" + rating + '\'' + 
                ",name = '" + name + '\'' + 
                ",description = '" + description + '\'' + 
                ",episode = '" + episode + '\'' + 
                "}";
            }
    }

  1. my api method
define globle

private List<DataResponse> dataResponses = new ArrayList<>();


    private void volleyAutomation(String url) {
            JSONArray array = new JSONArray();
            JsonArrayRequest request_json = new JsonArrayRequest(Request.Method.GET, url, array,
                    new Response.Listener<JSONArray>() {
                        @Override
                        public void onResponse(JSONArray response) {
    
                            GsonBuilder gsonBuilder = new GsonBuilder();
                            Gson gson = gsonBuilder.create();
                           dataResponses = Arrays.asList(gson.fromJson(response.toString(), DataResponse[].class));
    
                            rvList(dataResponses);
                            Log.d("respknce___", String.valueOf(dataResponses.size()));
                        }
                    }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError volleyError) {
    
                }
            });
            RequestQueue requestQueue = Volley.newRequestQueue(getApplicationContext());
            requestQueue.add(request_json);
        }