개조가 BEGIN_OBJECT로 예상되었지만 BEGIN_ARRAY였습니다.


79

저는 JSON 구문 분석을 처음 사용하고 Square의 Retrofit 라이브러리를 사용하고 있으며이 문제에 직면했습니다.

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

내 모델은 다음과 같습니다.

public class Contacts {

    public List<User> contacts;

}

...

public class User {

    String username;
    String regid;

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

}

내 인터페이스 :

public interface ContactsInterface {

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

}

내 성공 방법 :

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

내 성공 방법에 사용하면 오류가 발생합니다.

BEGIN_OBJECT가 필요하지만 1 행 2 열에서 BEGIN_ARRAY였습니다.

여기서 무엇이 잘못 되었습니까?

답변:


168

지금은 다음과 같은 형식으로 응답을 구문 분석하고 있습니다.

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

예외는 루트에서 객체를 기대하지만 실제 데이터는 실제로 배열이라는 점에서이를 알려줍니다. 즉, 유형을 배열로 변경해야합니다.

가장 쉬운 방법은 콜백에서 직접 유형으로 목록을 사용하는 것입니다.

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

2
같은 방법으로 @AzlanJamal
더글러스 메스 퀴타에게

15

dependencies used :

compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'

json 응답은 array response또는 object response둘의 조합 일 수 있습니다 . 다음 세 가지 경우를 참조하십시오.

Case 1 : Parsing a json array response (OP의 경우)

이 경우는 json responses다음과 같은 형식에 적용됩니다.[{...} ,{...}]

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

먼저이 배열에 대한 모델 클래스를 생성 하거나 jsonschema2pojo로 이동 하여 아래와 같이 자동 생성합니다.

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

이 경우 다음과 같은 객체 목록을 반환해야합니다.

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

그런 retrofit2다음 다음과 같이 전화를 겁니다.

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() 개체 목록을 제공합니다

참조를 위해 다음 두 가지 사례를 확인할 수도 있습니다.

Case 2 : Parsing a json object response

이 사례는 {..} 형식의 json 응답에 적용됩니다.

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

여기에서는 object위의 예 와 동일 합니다. 따라서 모델 클래스는 동일하지만 위의 예와 같이 이러한 객체의 배열이 없습니다. 하나의 객체 만 있으므로 목록으로 구문 분석 할 필요가 없습니다.

따라서 다음과 같이 변경하십시오. object response

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

그런 retrofit2다음 다음과 같이 전화를 겁니다.

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() 당신에게 물건을 줄 것입니다

json 객체 응답을 구문 분석하는 동안 일반적인 오류를 확인할 수도 있습니다. "expected begin_array but was begin_object"

Case 3 : Parsing a json array inside json object

이 경우는 json responses다음과 같은 형식에 적용됩니다.{"array_name":[{...} ,{...}]}

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

두 개의 객체 (하나는 배열 외부, 하나는 배열 내부)가 있으므로 여기에 두 개의 모델 클래스가 필요합니다.

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

}

Contacts.java위의 생성 된 객체를 목록 객체에 사용할 수 있습니다 (케이스 1에 대해 생성됨).

따라서 다음과 같이 변경하십시오. object response

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

그런 retrofit2다음 다음과 같이 전화를 겁니다.

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

여기서 사례 1과의 차이점 은 객체 목록을 가져 오는 response.body().getContacts()대신 사용해야한다는 것입니다.response.body()

위의 경우에 대한 일부 참조 :

사례 1 : json 배열 응답 파싱 , 사례 2 : json 객체 응답 파싱 , 혼합 : 다른 json 객체 내부에서 json 배열 파싱


hai 세 번째 형식으로 데이터를 요청하려고하는데 계속 같은 오류가 발생합니다. 도와주세요
Swapna

확인 모델 클래스에서 배열 이름의 철자가 응답의 그것과 정확히 일치하는지 확인 또한, 코드에 대한 자세한 정보를 제공하십시오
Navneet 크리슈나

8

인터페이스에서

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

이 코드로

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

1
이것은 Jake Wharton이 게시 한 설명을 제외한 동일한 답변 인 것으로 보입니다.
피터

그것은 사실, 저도 같은 오류를 가지고 있었고, POJO를 수정하기 전에 나는 같은 결과 ArrayList에를 생성하기로 결정
시스코 메자

1

목록으로 변환하십시오.

다음은 그 예입니다.

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

1

소스 코드 작동

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

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

이 같은 시도했지만 내가 같은 response.body ()를 얻고 [] 비우 .... 그 안에 데이터가 도움을 기쁘게하지 나
Swapna

0

MPV를 사용 하여 Deserializer에 다음을 넣으십시오.

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

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

이것이 문제를 해결하는 이유에 대한 설명을 제공하십시오
sorak

서버 응답에 이와 같은 JSON 형식이있는 {[..]}경우 개조가 올바르게 반복 될 수 없으며 Jake Wharton이 제안한대로 접두사를 JSON에 배치해야합니다. 최종 JSON은 이와 같고 {"data":[..]}문제가 해결됩니다.
JoseDuin

0

여기 스택은 Kotlin, Retrofit2, RxJava이며 일반 Call메서드에서 마이그레이션하고 있습니다.

내가 만든 서비스는 던지고 메시지 com.google.gson.JsonSyntaxExceptionjava.lang.IllegalStateException함께 :

Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column2

그러나 내가 찾을 수있는 모든 대답은 이것이 이미 서비스에 배열 유형이 없기 때문에 발생했다고 말했습니다 . 내 Kotlin 서비스는 다음과 같습니다.

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

문제는 Result이전 Call기반 솔루션을 사용할 때 넣은 내부였습니다 .

그것을 제거하면 문제가 해결되었습니다. 또한 서비스 호출 사이트에서 두 가지 오류 처리 방법의 서명을 변경해야했습니다.

/// WORKING SERVICE
interface InventoryService {

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

그리고 서비스를 사용하는 호출 사이트 조각 코드 :

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

위의 코드가 약간 변경되었습니다. 특히 Retrofit2 ConverterFactory를 사용하여 날짜를 문자열 대신 OffsetDateTime 개체로 전달할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.