Java 改造预期为 BEGIN_OBJECT 但为 BEGIN_ARRAY

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24154917/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 10:31:33  来源:igfitidea点击:

Retrofit Expected BEGIN_OBJECT but was BEGIN_ARRAY

javaandroidjsongsonretrofit

提问by Jezer Crespo

I'm fairly new to JSON parsing, I'm using the Retrofit library of Square and ran into this problem.

我对 JSON 解析相当陌生,我正在使用 Square 的 Retrofit 库并遇到了这个问题。

I'm trying to parse this JSON reponse:

我正在尝试解析这个 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"
      }
]

Here are my models:

这是我的模型:

public class Contacts {

    public List<User> contacts;

}

...

...

public class User {

    String username;
    String regid;

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

}

my Interface:

我的界面:

public interface ContactsInterface {

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

}

my success method:

我的成功方法:

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

When I use it on my success method it throws the error

当我在我的成功方法中使用它时,它会引发错误

Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column2

预期为 BEGIN_OBJECT 但在第 1 行 column2 为 BEGIN_ARRAY

What is wrong here?

这里有什么问题?

采纳答案by Jake Wharton

Right now you are parsing the response as if it was formatted like this:

现在您正在解析响应,就像它的格式如下:

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

The exception tells you this in that you are expecting an object at the root but the real data is actually an array. This means you need to change the type to be an array.

异常告诉您这一点,因为您期望根中有一个对象,但实际数据实际上是一个数组。这意味着您需要将类型更改为数组。

The easiest way is to just use a list as the direct type in the callback:

最简单的方法是使用列表作为回调中的直接类型:

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

回答by kkashyap1707

Convert it into a list.

将其转换为列表。

Below is the example:

下面是示例:

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

回答by Francisco MEza

in your interface replace

在您的界面中替换

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

By this code

通过这段代码

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

回答by Keshav Gera

Source Code Working

源代码工作

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

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

回答by JoseDuin

Using MPV, in your Deserializer, put this

使用MPV,在你的解串器中,把这个

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

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

回答by Navneet Krishna

dependencies used :

dependencies used :

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

json responses can be an array responseor an object responseor even a combination of both. See the following three cases

json 响应可以是两者的array response一个object response或什至它们的组合。看下面三种情况

Case 1 : Parsing a json array response(OP's case)

Case 1 : Parsing a json array response(OP的情况)

This case applies to those json responseswhich are of the form [{...} ,{...}]

这种情况适用于json responses形式为[{...} ,{...}]

E.g.

例如

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

First create a model class for this array or just goto jsonschema2pojoand auto generate one like below

首先为这个数组创建一个模型类,或者只是转到jsonschema2pojo并自动生成一个如下所示

Contacts.java

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

ContactsInterface

In this case you should return a list of objects like the following

在这种情况下,您应该返回如下所示的对象列表

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

Then make the retrofit2call like the following

然后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()will give you the list of objects

response.body()会给你对象列表

YOU MAY ALSO CHECK THE FOLLOWING TWO CASES FOR REFERENCE

您还可以查看以下两个案例以供参考

Case 2 : Parsing a json object response

Case 2 : Parsing a json object response

This case applies to those json responses which are of the form {..}

这种情况适用于那些格式为 {..} 的 json 响应

E.g.

例如

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

Here, we have the same objectas above example. So the model class will be the same, but like above example we dont have an array of these objects - just one single object and hence we dont need to parse it as a list.

在这里,我们与object上面的例子相同。所以模型类将是相同的,但像上面的例子一样,我们没有这些对象的数组——只有一个对象,因此我们不需要将它解析为列表。

So make the following changes for an object response

因此,对一个进行以下更改 object response

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

Then make the retrofit2call like the following

然后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()will give you the object

response.body()会给你对象

You may also check a common error while parsing json object response :"expected begin_array but was begin_object"

您还可以在解析 json 对象响应时检查常见错误:“预期 begin_array 但为 begin_object”

Case 3 : Parsing a json array inside json object

Case 3 : Parsing a json array inside json object

This case applies to those json responseswhich are of the form {"array_name":[{...} ,{...}]}

这种情况适用于json responses形式为{"array_name":[{...} ,{...}]}

E.g.

例如

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

You will need two model classes here since we have two objects(one outside and one inside the array).Generate it like below

您将需要两个模型类,因为我们有两个对象(一个在数组外,一个在数组内)。生成它,如下所示

ContactWrapper

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

}

You can use Contacts.javagenerated above for the list objects(generated for case 1)

您可以使用Contacts.java上面生成的列表对象(为案例 1 生成)

So make the following changes for an object response

因此,对一个进行以下更改 object response

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

Then make the retrofit2call like the following

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

Here, the difference from case 1 is that we should use response.body().getContacts()instead of response.body()to get the list of objects

在这里,与案例 1 的不同之处在于我们应该使用response.body().getContacts()而不是response.body()来获取对象列表

Some references for above cases :

以上案例的一些参考:

case 1 : Parsing a json array response, case 2 : Parsing a json object response, mixed : Parsing json array inside another json object

案例 1:解析 json 数组响应,案例 2:解析 json 对象响应,混合:解析另一个 json 对象内的 json 数组

回答by Sez

The stack here is Kotlin, Retrofit2, RxJava and we're migrating to that off regular Callmethods.

这里的堆栈是 Kotlin、Retrofit2、RxJava,我们正在迁移到常规Call方法之外。

The service that I had created was throwing com.google.gson.JsonSyntaxExceptionand java.lang.IllegalStateExceptionwith message:

我创建的服务正在抛出com.google.gson.JsonSyntaxExceptionjava.lang.IllegalStateException带有消息:

Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column2

But all the answers I could find said that this was caused by not having an arraytype in the service, which I already did. My Kotlin service looked like this:

但是我能找到的所有答案都说这是由于服务中没有数组类型引起的,我已经这样做了。我的 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>>>
}

The problem was the Resultin there, which I had put in when I was using an earlier Callbased solution.

问题出Result在那里,这是我在使用较早Call的解决方案时放入的。

Removing it fixed the problem. I also had to change the signature of the two error handling methods at my call-site for the service:

删除它解决了问题。我还必须在我的服务调用站点更改两种错误处理方法的签名:

/// WORKING SERVICE
interface InventoryService {

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

And the call-site fragment code that uses the service:

以及使用该服务的调用站点片段代码:

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

Note that the above code has been changed a little. In particular I used a Retrofit2 ConverterFactoryto allow the dates to be passed in as OffsetDateTime objects instead of strings.

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