预期进行BEGIN_OBJECT的改造,但进行BEGIN_ARRAY


78

我是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行column2处为BEGIN_ARRAY

怎么了

Answers:


166

现在,您正在解析响应,就好像它是这样格式化的:

{
  "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对象响应时检查一个常见错误:“预期的begin_array但为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 Krishna

8

在您的界面中替换

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

通过此代码

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

1
这似乎是相同的答案,减去杰克·沃顿(Jake Wharton)发布的描述。
彼得

没错,我遇到了同样的错误,在修改pojo之前,我决定创建一个arrayList并获得相同的结果
Francisco MEza

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在您的反序列化器中,将其

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

请注意,以上代码已稍作更改。特别是,我使用Retrofit2ConverterFactory允许将日期作为OffsetDateTime对象而不是字符串传递。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.