使用Retrofit和RxJava,当它不直接映射到模型对象时,如何反序列化JSON? [英] Using Retrofit and RxJava, how do I deserialize JSON when it doesn't map directly to a model object?

查看:233
本文介绍了使用Retrofit和RxJava,当它不直接映射到模型对象时,如何反序列化JSON?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用的API以平坦格式返回对象(及其包含的对象),我无法使用Retrofit和RxJava优雅地工作。



考虑针对 / employees / {id} 端点的此JSON响应:

  {
id:123,
id_to_name:{
123:John Doe
},
id_to_age:{
123:30
}
}

使用Retrofit和RxJava,我如何反序列化为名称 age



理想情况下,我希望RxJava的 onNext 方法用 Employee 对象调用。这可能吗?这可能是用某种类型的自定义反序列化器子类(我现在使用Gson)?

我意识到我可以创建一个 EmployeeResponse 直接映射到JSON响应的对象,但必须将 EmployeeResponse 映射到 Employee 每次我在一个活动中使用这个对象似乎都是一种不幸。当平面响应也包含需要反序列化并设置为 Employee 中的字段的其他对象时,它也变得更加复杂。



有没有更好的方法?

解决方案

完整的解决方案这看起来好像很多,但这会让你用 Employee 而不是 EmployeeResponse 编写Retrofit接口。以下是游戏计划:


  • 您仍然需要 EmployeeResponse Employee 对象,其中 EmployeeResponse 恰好映射到您从API获得的内容。将响应视为 Employee 的构建器,并编写一个静态工厂方法,该方法从<$ c $返回 Employee c> EmployeeResponse ,即。 Employee employee = Employee.newInstance(response);

  • 您将创建一个自定义 TypeAdapterFactory 为Gson。当Gson看到你请求一个 Employee 对象时,我们将使TypeAdapter实际创建一个 EmployeeResponse ,然后返回 Employee 通过上面描述的静态工厂方法。



TypeAdapterFactory看起来像这样:

  public class EmployeeAdapterFactory实现TypeAdapterFactory {
@Override public< T> TypeAdapter< T> create(Gson gson,TypeToken< T> type){
return type.getRawType()== Employee.class
? (TypeAdapter< T>)employeeAdapter(gson,(TypeToken< Employee>)类型)
:null;
}

Private TypeAdapter< Employee> employeeAdapter(Gson gson,TypeToken< Employee> type){
return new TypeAdapter< Employee>(){
@Override public void write(JsonWriter out,Employee value)throws IOException {
//如果需要,TODO序列化逻辑从Employee返回EmployeeResponse结构
}
$ b @Override public Employee read(JsonReader in)throws IOException {
return Employee.newInstance(gson .fromJson(in,EmployeeResponse.class));
}
};


$ / code>

在制作Gson时注册工厂:

  Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new EmployeeAdapterFactory())
.build();

Retrofit retrofit = new Retrofit.Builder()。baseUrl(https://foo.bar)
.addConverterFactory(GsonConverterFactory.create(gson))
.. 。/ /您的其他Retrofit配置,如RxJava调用适配器工厂
.build();

现在您可以安全地定义您的所有Retrofit界面, Employee 而不是 EmployeeResponse


The API I'm working with returns objects (and their containing objects) in a "flat" format and I'm having trouble getting this to work elegantly with Retrofit and RxJava.

Consider this JSON response for an /employees/{id} endpoint:

{
    "id": "123",
    "id_to_name": {
        "123" : "John Doe"
    },
    "id_to_age": {
        "123" : 30
    }
}

Using Retrofit and RxJava, how do I deserialize this to a Employee object with fields for name and age?

Ideally I'd like RxJava's onNext method to be called with an Employee object. Is this possible? Could this perhaps be done with some type of custom deserializer subclass (I'm using Gson at the moment)?

I realize I could create an EmployeeResponse object that maps directly to the JSON response, but having to map the EmployeeResponse to the Employee object every time I use this in an activity seems kind of unfortunate. It also gets much more complicated when the flat response also contains other objects that need to get deserialized and set as fields on the Employee.

Is there a better way?

解决方案

The complete solution to this will seem like a lot, but this will let you write Retrofit interfaces with Employee instead of EmployeeResponse. Here's the game plan:

  • You will still need both EmployeeResponse and Employee objects, where EmployeeResponse just maps exactly to what you'd get from the API. Treat the response as a builder for Employee and write a static factory method that returns an Employee from an EmployeeResponse, ie. Employee employee = Employee.newInstance(response);
  • You will be creating a custom TypeAdapterFactory for Gson. When Gson sees you request a Employee object, we will have the TypeAdapter actually create an EmployeeResponse, then return the Employee via the static factory method described above.

Your TypeAdapterFactory will look something like this:

public class EmployeeAdapterFactory implements TypeAdapterFactory {
  @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
    return type.getRawType() == Employee.class
           ? (TypeAdapter<T>) employeeAdapter(gson, (TypeToken<Employee>) type)
           : null;
  }

  private TypeAdapter<Employee> employeeAdapter(Gson gson, TypeToken<Employee> type) {
    return new TypeAdapter<Employee>() {
      @Override public void write(JsonWriter out, Employee value) throws IOException {
        // TODO serialization logic to go from an Employee back to EmployeeResponse structure, if necessary
      }

      @Override public Employee read(JsonReader in) throws IOException {
        return Employee.newInstance(gson.fromJson(in, EmployeeResponse.class));
      }
    };
  }
}

Register the factory when you make Gson:

Gson gson = new GsonBuilder()
    .registerTypeAdapterFactory(new EmployeeAdapterFactory())
    .build();

Retrofit retrofit = new Retrofit.Builder().baseUrl("https://foo.bar")
    .addConverterFactory(GsonConverterFactory.create(gson))
    ... // your other Retrofit configs, like RxJava call adapter factory
    .build();

And now you can safely define all your Retrofit interfaces with Employee instead of EmployeeResponse.

这篇关于使用Retrofit和RxJava,当它不直接映射到模型对象时,如何反序列化JSON?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆