首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何使用Retrofit和GSON解析由[]包围的JSON对象列表?

如何使用Retrofit和GSON解析由[]包围的JSON对象列表?
EN

Stack Overflow用户
提问于 2016-04-16 05:07:10
回答 3查看 27.2K关注 0票数 30

我已经创建了一个简单的REST端点:

代码语言:javascript
复制
http://<server_address>:3000/sizes

此URL返回一个非常简单的响应,其中包含一个json数组,如下所示:

代码语言:javascript
复制
[
  { "id": 1, "name": "Small", "active": true },
  { "id": 2, "name": "Medium", "active": true },
  { "id": 3, "name": "Large", "active": true }
]

现在,我正在尝试使用带有GSON的Retrofit 2来消费这个响应。

我添加了一个模型:

代码语言:javascript
复制
@lombok.AllArgsConstructor
@lombok.EqualsAndHashCode
@lombok.ToString
public class Size {
    private int id;
    private String name;
    private boolean active;

    @SerializedName("created_at")
    private String createdAt;

    @SerializedName("updated_at")
    private String updatedAt;
}

和服务:

代码语言:javascript
复制
public interface Service {
    @GET("sizes")
    Call<List<Size>> loadSizes();
}

我已经实例化了一个Retrofit:

代码语言:javascript
复制
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("http://<server_address>:3000")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

我的服务是:

代码语言:javascript
复制
Service service = retrofit.create(Service.class);

现在,尝试调用数据:

代码语言:javascript
复制
service.loadSizes().enqueue(new Callback<List<Size>>() {
    @Override
    public void onResponse(Call<List<Size>> call, Response<List<Size>> response) {
        for(Size size: response.body()) {
            System.out.println(size.toString());
        }
    }

    @Override
    public void onFailure(Call<List<Size>> call, Throwable t) {
        System.out.println(t.getMessage());
    }
});

最终出现异常的是:

java.lang.IllegalStateException:应为BEGIN_OBJECT,但在第1行第18列路径$.name处为字符串

我认为错误是因为REST API返回的响应既不是对象,也不是数组。

  1. 我说得对吗?
  2. 让这段代码正常工作的最简单方法是什么?

REST服务不能修改为,因此响应必须保持原样。

此外,使用纯GSON对上面的json进行反序列化可以通过以下方式完成:

代码语言:javascript
复制
Type sizesType = new TypeToken<List<Size>>(){}.getType();
List<Size> size = new Gson().fromJson(json, sizesType);

但是我不知道如何让Retrofit 2使用它。

提前谢谢。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2016-04-20 08:22:31

有趣的是..。我的代码完全没问题。至少是上面问题中提到的那个。

我最终从我的Size模型中删除了一行。

当我专注于代码本身(特别是Retrofit的配置)时,我完全忽略了导入。

当我开始在模型的字段中输入String类时,我实现了Size模型:

  • name
  • createdAt
  • updatedAt

IntelliJ IDEA的代码补全建议我使用

  • 不是java.lang.String
  • ,而是com.sun.org.apache.xpath.internal.operations.String

完全搞砸了 Gson**'s反序列化的**。

当涉及到奖励的时候...

我决定将我自己的答案标记为有效。为什么?

  • 确保,每个开发人员都会遇到与me -完全相同的麻烦,请确保您拥有有效的imports.

非常感谢上面的先生们,感谢他们的伟大服务。

因为我只有一个赏金,所以我决定奖励xiaoyaoworm,因为他的代码更符合我的需求(我还没有在我的问题中写出来,但是编写这样简单的服务的想法-就像我在问题中提到的那样-是为了对最终用户的实现细节隐藏起来,并且不在BNK响应中使用JsonArray之类的东西)。

更新1:

xiaoyaoworm的答案唯一的问题是,他建议Size模型不需要任何注释,这对于引用的示例来说是完全错误的。

对于上述情况,Size模型的确切两个字段需要注释- created_atupdated_at

我甚至测试了几个版本的converter-gson库(我看到xiaoyaoworm使用的不是我)--它没有改变任何东西。注释是必要的。

否则--再一次,非常感谢!

票数 5
EN

Stack Overflow用户

发布于 2016-04-20 04:24:43

最近,我刚刚完成了一个与retrofit2相关的项目。根据我的源码,我把你所有的东西都复制到我的项目中去试一试,做了一些小改动,它在我这边运行得很好。

在您的build.gradle中,添加以下内容:

代码语言:javascript
复制
 compile 'com.squareup.retrofit2:retrofit:2.0.1'
 compile 'com.google.code.gson:gson:2.6.2'
 compile 'com.squareup.okhttp3:okhttp:3.1.2'
 compile 'com.squareup.retrofit2:converter-gson:2.0.1'
 compile 'com.squareup.okhttp3:logging-interceptor:3.2.0'

模型:(更新:跟随tommus的案例,createdAt和updatedAt现在显示在他的json response示例中,由于模型中的名称与json response不同,这两个值需要注释)

代码语言:javascript
复制
public class Size {
    private int id;
    private String name;
    private boolean active;

    @SerializedName("created_at")
    private String createdAt;

    @SerializedName("updated_at")
    private String updatedAt;
}

服务:(与您所拥有的完全相同)

代码语言:javascript
复制
public interface service {
    @GET("sizes")
    Call<List<Size>> loadSizes();    
}

RestClient:(我在这里添加了日志,这样你就可以看到所有的请求信息和响应信息,注意不要使用本地主机,而是在URL中使用您的服务器ip地址)

代码语言:javascript
复制
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.xiaoyaoworm.prolificlibrary.test.Service;

import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class RestClient {

    private static Service service;

    public static Service getClient() {
        if (service == null) {
            Gson gson = new GsonBuilder()
                    .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
                    .create();

            // Add logging into retrofit 2.0
            HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
            logging.setLevel(HttpLoggingInterceptor.Level.BODY);
            OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
            httpClient.interceptors().add(logging);

            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl("http://YOURSERVERIPNOTLOCALHOST:3000/")
                    .addConverterFactory(GsonConverterFactory.create(gson))
                    .client(httpClient.build()).build();

            service = retrofit.create(Service.class);
        }
        return service;
    }
}

在您的活动中,添加以下函数来运行您的代码:(与您所做的完全相同。响应将是您大小列表()

代码语言:javascript
复制
   private void loadSize() {
        Service serviceAPI = RestClient.getClient();
        Call<List<Size>> loadSizeCall = serviceAPI.loadSizes();
        loadSizeCall.enqueue(new Callback<List<Size>>() {
            @Override
            public void onResponse(Call<List<Size>> call, Response<List<Size>> response) {
                for(Size size: response.body()) {
                    System.out.println(size.toString());
                }
            }

            @Override
            public void onFailure(Call<List<Size>> call, Throwable t) {
                System.out.println(t.getMessage());
            }
        });
    }

运行此命令,您将看到要打印的信息:

这是我的github代码库,我使用了改进2.0来使GET、POST、PUT、DELETE工作变得简单。您可以将此作为参考。My Github retrofit2.0 repo

票数 19
EN

Stack Overflow用户

发布于 2016-04-19 23:31:53

请使用以下内容:

build.gradle文件:

代码语言:javascript
复制
dependencies {
    ...
    compile 'com.squareup.retrofit2:retrofit:2.0.1'
    compile 'com.squareup.retrofit2:converter-gson:2.0.1'
    compile 'com.google.code.gson:gson:2.6.2'
}

WebAPIService.java:

代码语言:javascript
复制
public interface WebAPIService {
    @GET("/json.txt") // I use a simple json file to get the JSON Array as yours
    Call<JsonArray> readJsonArray();
}

Size.java:

代码语言:javascript
复制
public class Size {
    @SerializedName("id")
    private int id;

    @SerializedName("name")
    private String name;

    @SerializedName("active")
    private boolean active;

    @SerializedName("created_At")
    private String createdAt;

    @SerializedName("updated_at")
    private String updatedAt;
}

MainActivity.java:

代码语言:javascript
复制
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("http://...")
            .addConverterFactory(GsonConverterFactory.create())
            .build();

    WebAPIService service = retrofit.create(WebAPIService.class);
    Call<JsonArray> jsonCall = service.readJsonArray();
    jsonCall.enqueue(new Callback<JsonArray>() {
        @Override
        public void onResponse(Call<JsonArray> call, Response<JsonArray> response) {
            String jsonString = response.body().toString();
            Log.i("onResponse", jsonString);
            Type listType = new TypeToken<List<Size>>() {}.getType();
            List<Size> yourList = new Gson().fromJson(jsonString, listType);
            Log.i("onResponse", yourList.toString());
        }

        @Override
        public void onFailure(Call<JsonArray> call, Throwable t) {
            Log.e("onFailure", t.toString());
        }
    });
}

以下是调试屏幕截图:

更新:您还可以使用以下选项:

代码语言:javascript
复制
@GET("/json.txt")
Call<List<Size>> readList();

代码语言:javascript
复制
    Call<List<Size>> listCall1 = service.readList();
    listCall1.enqueue(new Callback<List<Size>>() {
        @Override
        public void onResponse(Call<List<Size>> call, Response<List<Size>> response) {
            for (Size size : response.body()){
                Log.i("onResponse", size.toString());
            }
        }

        @Override
        public void onFailure(Call<List<Size>> call, Throwable t) {
            Log.e("onFailure", t.toString());
        }
    });
票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36656827

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档