use*_*435 13 android gson deserialization retrofit
我正在使用Retrofit 2和Gson,我无法从我的API反序列化响应.这是我的情景:
我有一个名为模型对象Employee,它有三个字段:id,name,age.
我有一个返回如下单个Employee对象的API :
{
"status": "success",
"code": 200,
"data": {
"id": "123",
"id_to_name": {
"123" : "John Doe"
},
"id_to_age": {
"123" : 30
}
}
}
Run Code Online (Sandbox Code Playgroud)
以及Employee像这样的对象列表:
{
"status": "success",
"code": 200,
"data": [
{
"id": "123",
"id_to_name": {
"123" : "John Doe"
},
"id_to_age": {
"123" : 30
}
},
{
"id": "456",
"id_to_name": {
"456" : "Jane Smith"
},
"id_to_age": {
"456" : 35
}
},
]
}
Run Code Online (Sandbox Code Playgroud)
这里有三个主要的事情需要考虑:
data字段内.id_to_age需要的值映射到age模型上的字段)dataAPI响应中的字段可以是单个对象,也可以是对象列表.如何实现反序列化,Gson以便优雅地处理这三种情况?
理想情况下,我宁愿完全使用TypeAdapter或TypeAdapterFactory代替支付性能损失JsonDeserializer.最后,我想用一个实例来结束Employee或List<Employee>使得它满足这个接口:
public interface EmployeeService {
@GET("/v1/employees/{employee_id}")
Observable<Employee> getEmployee(@Path("employee_id") String employeeId);
@GET("/v1/employees")
Observable<List<Employee>> getEmployees();
}
Run Code Online (Sandbox Code Playgroud)
我之前发布的这个问题讨论了我的第一次尝试,但它没有考虑上面提到的一些问题: 使用Retrofit和RxJava,当它不直接映射到模型对象时,如何反序列化JSON?
编辑:相关更新:创建自定义转换器工厂做工作 - 避免无限循环的关键ApiResponseConverterFactory是调用Retrofit nextResponseBodyConverter,它允许您指定要跳过的工厂.关键是这将是一个Converter.Factory注册Retrofit,而不是TypeAdapterFactoryGson.这实际上是更可取的,因为它可以防止ResponseBody的双重反序列化(不需要反序列化主体,然后再将其重新打包为另一个响应).
原始答案:
ApiResponseAdapterFactory除非您愿意将所有服务接口包装起来,否则该方法不起作用ApiResponse<T>.但是,还有另一种选择:OkHttp拦截器.
这是我们的策略:
ResponseResponse#body()将被反序列化为一个ApiResponse,我们返回一个新的Response,ResponseBody只是我们想要的内容.所以ApiResponse看起来像:
public class ApiResponse {
String status;
int code;
JsonObject data;
}
Run Code Online (Sandbox Code Playgroud)
ApiResponseInterceptor:
public class ApiResponseInterceptor implements Interceptor {
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
public static final Gson GSON = new Gson();
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
final ResponseBody body = response.body();
ApiResponse apiResponse = GSON.fromJson(body.string(), ApiResponse.class);
body.close();
// TODO any logic regarding ApiResponse#status or #code you need to do
final Response.Builder newResponse = response.newBuilder()
.body(ResponseBody.create(JSON, apiResponse.data.toString()));
return newResponse.build();
}
}
Run Code Online (Sandbox Code Playgroud)
配置OkHttp和Retrofit:
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new ApiResponseInterceptor())
.build();
Retrofit retrofit = new Retrofit.Builder()
.client(client)
.build();
Run Code Online (Sandbox Code Playgroud)
而Employee和EmployeeResponse应遵循的适配器工厂构造我在上一个问题中写道.现在ApiResponse拦截器应该使用所有字段,并且您进行的每个Retrofit调用应该只返回您感兴趣的JSON内容.
小智 6
我建议使用a,JsonDeserializer因为响应中没有那么多级别的嵌套,所以它不会是一个很大的性能损失.
类看起来像这样:
需要针对通用响应调整服务接口:
interface EmployeeService {
@GET("/v1/employees/{employee_id}")
Observable<DataResponse<Employee>> getEmployee(@Path("employee_id") String employeeId);
@GET("/v1/employees")
Observable<DataResponse<List<Employee>>> getEmployees();
}
Run Code Online (Sandbox Code Playgroud)
这是一般数据响应:
class DataResponse<T> {
@SerializedName("data") private T data;
public T getData() {
return data;
}
}
Run Code Online (Sandbox Code Playgroud)
员工模型:
class Employee {
final String id;
final String name;
final int age;
Employee(String id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
}
Run Code Online (Sandbox Code Playgroud)
员工解串器:
class EmployeeDeserializer implements JsonDeserializer<Employee> {
@Override
public Employee deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
JsonObject employeeObject = json.getAsJsonObject();
String id = employeeObject.get("id").getAsString();
String name = employeeObject.getAsJsonObject("id_to_name").entrySet().iterator().next().getValue().getAsString();
int age = employeeObject.getAsJsonObject("id_to_age").entrySet().iterator().next().getValue().getAsInt();
return new Employee(id, name, age);
}
}
Run Code Online (Sandbox Code Playgroud)
与响应的问题是,name和age包含一个JSON对象,因此它需要多一点的工作,以分析它whitch转换为Java中的地图内.
| 归档时间: |
|
| 查看次数: |
15115 次 |
| 最近记录: |