如何使用 Retrofit 和 GSON 解析被 [] 包围的 JSON 对象列表?
How to parse list of JSON objects surrounded by [] using Retrofit and GSON?
我创建了一个简单的 REST 端点:
http://<server_address>:3000/sizes
这个 URL returns 包含一个 json 数组 的非常简单的响应如下:
[
{ "id": 1, "name": "Small", "active": true },
{ "id": 2, "name": "Medium", "active": true },
{ "id": 3, "name": "Large", "active": true }
]
现在,我正在尝试使用带有 GSON 的 Retrofit 2 使用此响应。
我添加了一个模型:
@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;
}
和服务:
public interface Service {
@GET("sizes")
Call<List<Size>> loadSizes();
}
我已经实例化了一个 Retrofit:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://<server_address>:3000")
.addConverterFactory(GsonConverterFactory.create())
.build();
我的服务:
Service service = retrofit.create(Service.class);
现在,正在尝试调用数据:
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: Expected BEGIN_OBJECT but was STRING at line 1 column 18 path $[0].name
我想这个错误是由 REST API returns 一个数组或对象 .
的响应引起的
- 我说的对吗?
- 使此代码起作用的最简单方法是什么?
无法修改 REST 服务,因此响应必须保持原样。
此外,使用纯 GSON 对上述 json 进行反序列化可以通过以下方式完成:
Type sizesType = new TypeToken<List<Size>>(){}.getType();
List<Size> size = new Gson().fromJson(json, sizesType);
但我不知道如何让 Retrofit 2 使用它。
提前致谢。
请使用以下内容:
build.gradle 文件:
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:
public interface WebAPIService {
@GET("/json.txt") // I use a simple json file to get the JSON Array as yours
Call<JsonArray> readJsonArray();
}
Size.java:
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:
@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());
}
});
}
调试截图如下:
更新:
您还可以使用以下选项:
@GET("/json.txt")
Call<List<Size>> readList();
和
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());
}
});
最近刚完成一个retrofit2相关的项目。根据我的来源,我将你的所有东西复制到我的项目中进行尝试,进行一些小改动,我这边效果很好。
在您的 build.gradle 中添加:
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 响应示例中,这两个值需要注释,因为模型中的名称不同于json回复)
public class Size {
private int id;
private String name;
private boolean active;
@SerializedName("created_at")
private String createdAt;
@SerializedName("updated_at")
private String updatedAt;
}
服务:(和你的一模一样)
public interface service {
@GET("sizes")
Call<List<Size>> loadSizes();
}
RestClient: (我在这里添加日志,这样你就可以看到所有的请求信息和响应信息,注意不要使用Localhost而是你的服务器ip URL 中的地址)
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;
}
}
在您的 activity 上,将此函数添加到 运行 您的代码中:(与您所做的完全相同。响应将是您的列表大小 )
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 存储库,我使用 retrofit2.0 使简单的 GET POST PUT DELETE 工作。您可以将其用作参考。 My Github retrofit2.0 repo
有趣的是...我的代码非常好。至少是上面问题中提出的那个。
我最终从 Size
模型中删除了一行。
由于我专注于代码本身(尤其是 Retrofit 的配置),所以我完全忽略了导入。
事实证明 - 在实施 Size
模型时,当我开始为模型的字段键入 String
class 时:
name
createdAt
updatedAt
IntelliJ IDEA 的 代码完成建议我
- 不是
java.lang.String
- 但是
com.sun.org.apache.xpath.internal.operations.String
什么完全搞砸了 Gson
的反序列化。
说到奖励...
我决定将我自己的答案标记为有效。为什么?
- 为确保每一位遇到与我完全相同的麻烦的开发人员 - 确保您有有效的导入。
非常感谢上面的先生们提供的优质服务。
因为我只有一份赏金,所以我决定奖励 xiaoyaoworm as his code better match my needs (I haven't written it in my question but the idea of writing such simple service - as I've presented in my question - is to hide from the end-user implementation details and not use JsonArray
and such like in BNK 响应)。
更新 1:
xiaoyaoworm 答案的唯一问题是,他建议 Size
模型 不需要任何注释 什么是完全错误的引用 JSON 例子。
对于上述情况,Size
模型的两个字段 需要注释 - created_at
和 updated_at
.
我什至测试了 converter-gson
库的几个版本(我看到 xiaoyaoworm 除了我以外还使用过)- 它没有任何改变。注释是必要的。
否则 - 再次感谢!
我创建了一个简单的 REST 端点:
http://<server_address>:3000/sizes
这个 URL returns 包含一个 json 数组 的非常简单的响应如下:
[
{ "id": 1, "name": "Small", "active": true },
{ "id": 2, "name": "Medium", "active": true },
{ "id": 3, "name": "Large", "active": true }
]
现在,我正在尝试使用带有 GSON 的 Retrofit 2 使用此响应。
我添加了一个模型:
@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;
}
和服务:
public interface Service {
@GET("sizes")
Call<List<Size>> loadSizes();
}
我已经实例化了一个 Retrofit:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://<server_address>:3000")
.addConverterFactory(GsonConverterFactory.create())
.build();
我的服务:
Service service = retrofit.create(Service.class);
现在,正在尝试调用数据:
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: Expected BEGIN_OBJECT but was STRING at line 1 column 18 path $[0].name
我想这个错误是由 REST API returns 一个数组或对象 .
的响应引起的- 我说的对吗?
- 使此代码起作用的最简单方法是什么?
无法修改 REST 服务,因此响应必须保持原样。
此外,使用纯 GSON 对上述 json 进行反序列化可以通过以下方式完成:
Type sizesType = new TypeToken<List<Size>>(){}.getType();
List<Size> size = new Gson().fromJson(json, sizesType);
但我不知道如何让 Retrofit 2 使用它。
提前致谢。
请使用以下内容:
build.gradle 文件:
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:
public interface WebAPIService {
@GET("/json.txt") // I use a simple json file to get the JSON Array as yours
Call<JsonArray> readJsonArray();
}
Size.java:
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:
@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());
}
});
}
调试截图如下:
更新: 您还可以使用以下选项:
@GET("/json.txt")
Call<List<Size>> readList();
和
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());
}
});
最近刚完成一个retrofit2相关的项目。根据我的来源,我将你的所有东西复制到我的项目中进行尝试,进行一些小改动,我这边效果很好。
在您的 build.gradle 中添加:
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 响应示例中,这两个值需要注释,因为模型中的名称不同于json回复)
public class Size {
private int id;
private String name;
private boolean active;
@SerializedName("created_at")
private String createdAt;
@SerializedName("updated_at")
private String updatedAt;
}
服务:(和你的一模一样)
public interface service {
@GET("sizes")
Call<List<Size>> loadSizes();
}
RestClient: (我在这里添加日志,这样你就可以看到所有的请求信息和响应信息,注意不要使用Localhost而是你的服务器ip URL 中的地址)
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;
}
}
在您的 activity 上,将此函数添加到 运行 您的代码中:(与您所做的完全相同。响应将是您的列表大小 )
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 存储库,我使用 retrofit2.0 使简单的 GET POST PUT DELETE 工作。您可以将其用作参考。 My Github retrofit2.0 repo
有趣的是...我的代码非常好。至少是上面问题中提出的那个。
我最终从 Size
模型中删除了一行。
由于我专注于代码本身(尤其是 Retrofit 的配置),所以我完全忽略了导入。
事实证明 - 在实施 Size
模型时,当我开始为模型的字段键入 String
class 时:
name
createdAt
updatedAt
IntelliJ IDEA 的 代码完成建议我
- 不是
java.lang.String
- 但是
com.sun.org.apache.xpath.internal.operations.String
什么完全搞砸了 Gson
的反序列化。
说到奖励...
我决定将我自己的答案标记为有效。为什么?
- 为确保每一位遇到与我完全相同的麻烦的开发人员 - 确保您有有效的导入。
非常感谢上面的先生们提供的优质服务。
因为我只有一份赏金,所以我决定奖励 xiaoyaoworm as his code better match my needs (I haven't written it in my question but the idea of writing such simple service - as I've presented in my question - is to hide from the end-user implementation details and not use JsonArray
and such like in BNK 响应)。
更新 1:
xiaoyaoworm 答案的唯一问题是,他建议 Size
模型 不需要任何注释 什么是完全错误的引用 JSON 例子。
对于上述情况,Size
模型的两个字段 需要注释 - created_at
和 updated_at
.
我什至测试了 converter-gson
库的几个版本(我看到 xiaoyaoworm 除了我以外还使用过)- 它没有任何改变。注释是必要的。
否则 - 再次感谢!