使用 Retrofit 和 RxJava,如果 JSON 没有直接映射到模型对象,我该如何反序列化?

Using Retrofit and RxJava, how do I deserialize JSON when it doesn't map directly to a model object?

API 我正在使用 "flat" 格式的 returns 对象(及其包含的对象),但我无法使其与 Retrofit 和RxJava.

考虑 /employees/{id} 端点的这个 JSON 响应:

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

使用 Retrofit 和 RxJava,如何将其反序列化为具有 nameage 字段的 Employee 对象?

理想情况下,我希望使用 Employee 对象调用 RxJava 的 onNext 方法。这可能吗?这也许可以通过某种类型的自定义反序列化器子类来完成(我目前正在使用 Gson)?

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

有没有更好的方法?

这个问题的完整解决方案看起来很多,但这将使您可以使用 Employee 而不是 EmployeeResponse 来编写 Retrofit 接口。这是游戏计划:

  • 您仍然需要 EmployeeResponseEmployee 对象,其中 EmployeeResponse 恰好映射到您从 API 中获得的内容。将响应视为 Employee 的构建器并编写一个静态工厂方法,该方法 return 是 EmployeeResponseEmployee,即。 Employee employee = Employee.newInstance(response);
  • 您将为 Gson 创建自定义 TypeAdapterFactory。当 Gson 看到您请求一个 Employee 对象时,我们会让 TypeAdapter 实际上创建一个 EmployeeResponse,然后 return 通过上述静态工厂方法创建 Employee

您的 TypeAdapterFactory 看起来像这样:

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));
      }
    };
  }
}

制作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();

现在您可以使用 Employee 而不是 EmployeeResponse.

安全地定义所有 Retrofit 接口