Java 如何使用 Retrofit 和 GSON 解析由 [] 包围的 JSON 对象列表?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/36656827/
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
How to parse list of JSON objects surrounded by [] using Retrofit and GSON?
提问by Tomasz Dzieniak
I've created a simple REST endpoint:
我创建了一个简单的 REST 端点:
http://<server_address>:3000/sizes
This URL returns a very simple response containing a json arrayas follows:
此 URL 返回一个非常简单的响应,其中包含一个json 数组,如下所示:
[
{ "id": 1, "name": "Small", "active": true },
{ "id": 2, "name": "Medium", "active": true },
{ "id": 3, "name": "Large", "active": true }
]
Now, I'm trying to consume this response using Retrofit 2 with GSON.
现在,我正在尝试使用带有 GSON 的 Retrofit 2来使用此响应。
I've added a model:
我添加了一个模型:
@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;
}
And service:
和服务:
public interface Service {
@GET("sizes")
Call<List<Size>> loadSizes();
}
I've instantiated a Retrofit:
我已经实例化了一个改造:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://<server_address>:3000")
.addConverterFactory(GsonConverterFactory.create())
.build();
And my service:
还有我的服务:
Service service = retrofit.create(Service.class);
Now, trying to call the data:
现在,尝试调用数据:
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());
}
});
What ends up with an exception:
什么以异常结束:
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRINGat line 1 column 18 path $[0].name
java.lang.IllegalStateException:预期为 BEGIN_OBJECT,但在第 1 行第 18 列路径 $[0].name 处为 STRING
I suppose the error is caused by that, the REST API returns a response which is an array nor object.
我想错误是由那个引起的,REST API 返回一个响应,它是一个数组或对象。
- Am I correct?
- What is the easiest way to make this code to work?
- 我对么?
- 使此代码工作的最简单方法是什么?
REST service cannot be modified, so the response must stay as is.
REST 服务无法修改,因此响应必须保持原样。
Also, deserialization of the above json using pure GSON might be done by:
此外,可以通过以下方式使用纯 GSON 对上述 json 进行反序列化:
Type sizesType = new TypeToken<List<Size>>(){}.getType();
List<Size> size = new Gson().fromJson(json, sizesType);
But I have no idea how to make Retrofit 2 to use this.
但我不知道如何让 Retrofit 2 使用它。
Thanks in advance.
提前致谢。
采纳答案by Tomasz Dzieniak
The funny fact is... My code is perfectly fine. At least the one presented in the question above.
有趣的事实是......我的代码非常好。至少是上述问题中提出的那个。
I've ended up removing one line from my Size
model.
我最终从我的Size
模型中删除了一行。
As I focused on the code itself (especially Retrofit's configuration) I've totally ignored imports.
当我专注于代码本身(尤其是 Retrofit 的配置)时,我完全忽略了导入。
It turned out - while implementing Size
model when I've started typing String
class for model's fields:
事实证明 -Size
当我开始String
为模型字段输入类时实现模型:
name
createdAt
updatedAt
name
createdAt
updatedAt
IntelliJ IDEA's code completion suggested me
IntelliJ IDEA 的代码补全建议我
- not
java.lang.String
- but
com.sun.org.apache.xpath.internal.operations.String
- 不是
java.lang.String
- 但
com.sun.org.apache.xpath.internal.operations.String
what totally messed up Gson
's deserialization.
什么完全搞砸了Gson
反序列化。
When it comes to rewards...
说到奖励...
I've decided to mark as valid my own answer. Why?
我决定将我自己的答案标记为有效。为什么?
- To ensure that, every of those developers, who will come with exactly same trouble as me - make sure you have valid imports.
- 为了确保这一点,每个开发人员都会遇到和我一样的麻烦——确保你有有效的导入。
Many thanks goes to gentlmen above for their great services.
非常感谢上面的先生们提供的优质服务。
As I have only one bounty I've decided reward xiaoyaowormas 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 BNKresponse).
由于我只有一个赏金,我决定奖励xiaoyaoworm因为他的代码更符合我的需求(我没有在我的问题中写过它,但是编写如此简单的服务的想法 - 正如我在我的问题中所提出的 - 是隐藏来自最终用户的实施细节,而不是JsonArray
在BNK响应中使用等)。
Update 1:
更新 1:
The only problem with xiaoyaoworm's answer is that, he suggested the Size
model do not need any annotationswhat is totally wrong for the quoted JSON example.
xiaoyaoworm的答案唯一的问题是,他建议Size
模型不需要任何注释,引用的 JSON 示例完全错误。
For above case, exact two fieldsof the Size
model needs annotations- created_at
and updated_at
.
对于上述情况,确切的两个字段中的Size
模型需要注解-created_at
和updated_at
。
I've even tested few versions of the converter-gson
library (I saw xiaoyaowormhave used other than me) - it hasn't changed anything. Annotations were necessary.
我什至测试了该converter-gson
库的几个版本(我看到xiaoyaoworm使用过其他人) - 它没有改变任何东西。注释是必要的。
Otherwise - again, many thanks!
否则 - 再次,非常感谢!
回答by BNK
Please use the following:
请使用以下内容:
build.gradle file:
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:
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:
大小.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:
主活动.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());
}
});
}
Here is the debug screenshot:
下面是调试截图:
UPDATE: You can also use the following option:
更新:您还可以使用以下选项:
@GET("/json.txt")
Call<List<Size>> readList();
and
和
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());
}
});
回答by xiaoyaoworm
Recently I just finish one project related to retrofit2. Based on my source, I copy all your stuff into my project to give a try, making some minor change, it works well on my side.
最近刚完成一个retrofit2相关的项目。根据我的来源,我将您的所有内容复制到我的项目中进行尝试,进行了一些小的更改,它对我来说效果很好。
In your build.gradle, add those:
在您的 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'
Model: (UPDATE: Follow tommus's case, createdAt and updatedAt is now shown in his json response sample, these two values needs annotation due to the name in the model is different from json respone)
模型:(更新:按照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;
}
Service: (Exactly same as what you have)
服务:(和你所拥有的完全一样)
public interface service {
@GET("sizes")
Call<List<Size>> loadSizes();
}
RestClient: (I add log here, so that you can see all the request info and response info, be careful not using Localhostbut your server ip address in the URL)
RestClient:(我在这里添加日志,以便您可以看到所有请求信息和响应信息,注意不要使用Localhost,而是在URL中使用您的服务器IP地址)
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;
}
}
On your activity, add this function to run your code: (exactly same as what you have done. The response will be your list of size)
在您的活动中,添加此函数以运行您的代码:(与您所做的完全相同。响应将是您的大小列表)
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());
}
});
}
Running this you will see the info you want to print out:
Here is my github repo which I use retrofit2.0 to make simple GET POST PUT DELETE work. You can use this as reference. My Github retrofit2.0 repo
这是我的github repo,我使用retrofit2.0 来制作简单的GET POST PUT DELETE 工作。您可以将此作为参考。我的 Github 改造 2.0 存储库