GSON에서 "BEGIN_OBJECT가 예상되었지만 BEGIN_ARRAY"가 발생합니까?


295

이와 같은 JSON 문자열을 구문 분석하려고합니다.

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

개체 목록으로

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

여기 내가 사용하는 객체 클래스가 있습니다.

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

        }

그러나 그것은 나를 던졌습니다.

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

어떤 아이디어를 어떻게 고쳐야합니까?


12
@Soni-맞지 않습니다. jsonlint.org로 이동하여 JSON을 복사 / 붙여 넣기하면 유효하다는 것을 알 수 있습니다.
Brian Roach

@Soni-아니요, "["및 "]"를 제거했지만 여전히 동일합니다. 내가 가진 문자열에 하나의 객체가 아닌 여러 객체가 포함되어 있기 때문에 더 많은 것으로 생각됩니다.
Roger Travis

무엇은하지 jstring코드에서 언급하는 것이 같은 모양을?
IgorGanapolsky 2016 년

배열에서 응답이 반환되면 List를 가져 오려고하면 내 문제를 해결한다고 생각합니다.
iamkdblue

답변:


331

문제는 당신이 Gson타입의 객체를 가지고 있다는 것입니다. 당신은하지 않습니다. 유형의 객체 배열이 있습니다. 당신은 그런 식으로 결과를 캐스트하고 마술처럼 작동하기를 기대할 수는 없습니다.)

이 사용 Gson방법은 다음을 처리하는 방법을 설명합니다.

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

이것은 작동합니다 :

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

그러나 이것은 더 낫습니다.

Type collectionType = new TypeToken<Collection<ChannelSearchEnum>>(){}.getType();
Collection<ChannelSearchEnum> enums = gson.fromJson(yourJson, collectionType);

아마 실제로. 객체의 배열로서, 타입은 런타임에 유지되므로 gson은 무엇을 찾을 지 알고 있습니다. 좋은 생각.
njzk2

3
TypoToken<Collection<Something>>컬렉션에 대한 + 1-컬렉션 (서브 클래스) 및 / 또는 Iterables가있을 때 배열을 사용하지 마십시오.
Philipp Reichart

선택한 obj / array를 구문 분석하는 것이 올바른 방법이라고 생각하십니까? 도움말 stackoverflow.com/questions/18140830/...
LOG_TAG

1
우리가 문자열로 만들고 싶다면? 예를 들어 String [] t = gson.fromJson (myJson, String []. class)와 같이 작성할 수 있습니다.
Sahin Yanlık

4
이 답을 느끼는 것은 끝나지 않았습니다!
EngineSense

45

문제는 유형의 객체를 요청 ChannelSearchEnum하지만 실제로 가지고있는 것은 유형의 객체입니다 List<ChannelSearchEnum>.

다음을 통해이를 달성 할 수 있습니다.

Type collectionType = new TypeToken<List<ChannelSearchEnum>>(){}.getType();
List<ChannelSearchEnum> lcs = (List<ChannelSearchEnum>) new Gson()
               .fromJson( jstring , collectionType);

1
어떤 종류 Type입니까? 무엇을 가져올까요?
smatthewenglish

4
@ S.Matthew_English 가장 가능성이 높음java.lang.reflect.Type

36

필자의 경우 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"}]

나는 "카테고리"와 "url_title"을 recycleview에 인쇄합니다.

데이텀 클래스

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

}

요청 인터페이스

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

그리고 마지막으로 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());
        }
    });
}
}

5
이러한 유형의 문제에 대한 최상의 답변
Nicky Manali

4
이것은 특히 개조 사용자의 질문에 완벽하게 대답합니다. 명확성을 찾는 사람에게는 가장 필요한 부분은 Call <List <Datum >> getJSON ()입니다.
Carlos Anyona

13

대안이 될 수 있습니다

귀하의 답변을 다음과 같이 보이게

myCustom_JSON 응답

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

대신에

server_JSON 응답

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

암호

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

이 후 그것은 단지 다른 것입니다 GSON Parsing


내 json 형식을 변경할 수 없으면 어떻게합니까? 발리의 gson 요청을 사용하여 모델 클래스를 설정하고 있습니다. 어떻게합니까? 감사합니다
Kaveesh Kanwal

@KaveeshKanwal이 스레드에서 제공되는 다른 솔루션을 시도해보십시오. 이것 외에는 전혀
모르겠습니다

8

에 따라 GSON 사용 설명서 , 당신은 할 수 없습니다.

컬렉션 제한

임의의 개체 컬렉션을 직렬화 할 수는 있지만 직렬화를 해제 할 수는 없습니다. 사용자가 결과 객체의 유형을 표시 할 방법이 없기 때문에


7
그는 임의의 객체를 가지고 있지 않으며, 행복하게 다룰 하나의 특정 유형의 객체를 Gson가지고 있습니다
Brian Roach

실제로, 나는 당신이했던 것처럼 TypeToken으로 답변을 작성하기 시작했습니다. 그러나 제네릭 유형은 런타임에 포함되어 있지 않기 때문에 그것이 어떻게 작동 할 수 있는지 보지 못했습니다. (내가 테스트하지는 않았지만).
njzk2

3

이것은 Json 배열 목록처럼 보이 ArrayList므로 데이터를 처리하는 데 사용 하는 것이 가장 좋습니다 . api 엔드 포인트에서 다음과 같이 배열 목록을 추가하십시오.

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

1

다음과 같이 Gson에 추가 응답 유형을 알려야합니다.

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

1

이것이 GSON을 사용하는 가장 좋은 방법인지 확실하지 않지만 저에게 효과적입니다. 다음과 같이 사용할 수 있습니다 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());
    }
}

문자열 만 있지만 double 또는 int가 있으면 넣을 getDouble수도 getInt있습니다.

IOHelper클래스 의 방법은 다음입니다 (여기에서 경로는 내부 저장소에 저장 됨).

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

이에 대한 자세한 정보를 원하는 경우에, 당신은 볼 수있는 이 비디오 내가 코드를 얻을 readJson(); 그리고 이 스레드가 어디의 코드를 얻을 getData().


0

코 틀린 :

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)

나는 아무것도 편집하거나 공감하지 않았다.
파쇄기
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.