【问题标题】:Deserialize JSON to POJO using Retrofit and Gson使用 Retrofit 和 Gson 将 JSON 反序列化为 POJO
【发布时间】:2015-11-04 21:29:11
【问题描述】:

我正在使用一个 API,它对单个用户资源做出如下响应:

{
  "data": {
    "id": 11,
    "first_name": "First",
    "last_name": "Last",
    "books": {
      "data": [
        {
          "id": 13,
          "name": "Halo"
        }
      ]
    },
    "games": {
      "data": [
        {
          "id": 1,
          "name": "Halo"
        }
      ]
    }
  }
}

或者像下面这样针对多个用户资源:

{
  "data": [
    {
      "id": 11,
      "first_name": "First",
      "last_name": "Last",
      "books": {
        "data": [
          {
            "id": 13,
            "name": "Halo"
          }
        ]
      },
      "games": {
        "data": [
          {
            "id": 1,
            "name": "Halo"
          }
        ]
      }
    },
  ],
  "meta": {
    "pagination": {
      "total": 11,
      "count": 10,
      "per_page": 10,
      "current_page": 1,
      "total_pages": 2,
      "links": {
        "next": "http://api.###.com/users?page=2"
      }
    }
  }
}

需要注意的关键事项是:

  1. 所有资源都嵌套在data 键下,单个作为对象或多个作为对象数组。这包括上面示例中的书籍和游戏等嵌套资源。
  2. 我需要能够为我的分页例程检索 meta 键的值

用户模型

public class User extends BaseModel {

    public Integer id;
    public String firstName;
    public String lastName;

    public List<Book> books; // These will not receive the deserialized 
    public List<Game> games; // JSON due to the parent data key

}

自定义 JSON 反序列化器

public class ItemTypeAdapterFactory implements TypeAdapterFactory {

    public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {

        final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
        final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);

        return new TypeAdapter<T>() {

            public void write(JsonWriter out, T value) throws IOException {
                delegate.write(out, value);
            }

            public T read(JsonReader in) throws IOException {

                JsonElement jsonElement = elementAdapter.read(in);
                if (jsonElement.isJsonObject()) {
                    JsonObject jsonObject = jsonElement.getAsJsonObject();
                    // If the data key exists and is an object or array, unwrap it and return its contents
                    if (jsonObject.has("data") && (jsonObject.get("data").isJsonObject() || jsonObject.get("data").isJsonArray())) {
                        jsonElement = jsonObject.get("data");
                    }
                }

                return delegate.fromJsonTree(jsonElement);
            }
        }.nullSafe();
    }
}

一切正常,但我不知道如何访问meta 键进行分页。

理想情况下,我会让 Gson 反序列化对以下 POJO 的响应:

public class ApiResponse {
    public Object data;
    public Meta meta
}

我可以在响应回调中将 response 字段转换为正确的类型,如下所示:

Map<String, String> params = new HashMap<String, String>();
params.put("include", "books,games");
ApiClient.getClient().authenticatedUser(params, new ApiClientCallback<ApiResponse>() {
    @Override
    public void failure(RestError restError) {
        Log.d("TAG", restError.message);
    }

    @Override
    public void success(ApiResponse response, Response rawResponse) {
        User user = (User) response.data; // Cast data field to User type
        Log.d("TAG", user.firstName);
        Log.d("TAG", "Total pages" + response.meta.pagination.total.toString()); // Still have access to meta key data
    }
});

但是ApiResponse 对象的data 字段是null

我的 Java 非常生锈,我不知道这是否可能,也不知道如何正确处理,非常感谢任何帮助。

【问题讨论】:

    标签: java json gson retrofit json-deserialization


    【解决方案1】:

    我需要同样的东西,并通过在您的自定义序列化程序中添加 if 语句来使其工作:

    …
        // If the meta key exists, consider the element to be root and don't unwrap it
        if (!jsonObject.has("meta")) {
            // If the data key exists and is an object or array, unwrap it and return its contents
            if (jsonObject.has("data") && (jsonObject.get("data").isJsonObject() || jsonObject.get("data").isJsonArray())) {
                jsonElement = jsonObject.get("data");
            }
        }
    …
    

    ApiResponsedata 字段之所以为 null,是因为您的原始反序列化程序正在处理整个响应,并使您“松散”根对象的 datameta 元素。

    我还对 ApiResponse 类进行了参数化:

    public class ApiResponse<T> {
        public Meta meta;
        public T data;
    }
    

    这样反序列化仍然可以工作,而无需创建许多不同的响应类,而不再需要强制转换,您可以根据需要指定 ApiResponsedata 字段的类型(例如,ApiResponse&lt;User&gt; 用于单个用户资源, ApiResponse&lt;List&lt;User&gt;&gt; 用于多个用户资源等)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-02-02
      • 2017-05-13
      • 1970-01-01
      • 2018-03-28
      • 1970-01-01
      • 2019-03-30
      • 2017-08-09
      • 2012-04-07
      相关资源
      最近更新 更多