[英]retrofit gson converter for nested json with different objects
I've JSON structure like follows - 我的JSON结构如下 -
{
"status": true,
"message": "Registration Complete.",
"data": {
"user": {
"username": "user88",
"email": "user@domain.com",
"created_on": "1426171225",
"last_login": null,
"active": "1",
"first_name": "User",
"last_name": "",
"company": null,
"phone": null,
"sign_up_mode": "GOOGLE_PLUS"
}
}
}
Above format is common . 以上格式很常见。 Only
data
key can hold different types of information like user
, product
, invoice
etc. 只有
data
键可以容纳不同类型的信息,如user
, product
, invoice
等。
I want to keep status
, message
and data
keys same in every rest response. 我想在每个休息响应中保持
status
, message
和data
键相同。 data
will be treated according to status
and message
will be displayed to user. data
将根据待治疗status
和message
将被显示给用户。
So basically, above format is desired in all apis. 所以基本上,所有api都需要以上格式。 Only information inside
data
key will be different each time. 每次只有
data
键内的data
才会不同。
And I've setup a following class and set it up as gson converter - MyResponse.java 我已经设置了以下类并将其设置为gson converter - MyResponse.java
public class MyResponse<T> implements Serializable{
private boolean status ;
private String message ;
private T data;
public boolean isStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
Deserializer.java Deserializer.java
class Deserializer<T> implements JsonDeserializer<T>{
@Override
public T deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException{
JsonElement content = je.getAsJsonObject();
// Deserialize it. You use a new instance of Gson to avoid infinite recursion to this deserializer
return new Gson().fromJson(content, type);
}
}
And used it as follows - 并使用如下 -
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
gsonBuilder.registerTypeAdapter(MyResponse.class, new Deserializer<MyResponse>());
...... ..... ....
restBuilder.setConverter(new GsonConverter(gsonBuilder.create()));
Service interface is as follows - 服务界面如下 -
@POST("/register")
public void test1(@Body MeUser meUser, Callback<MyResponse<MeUser>> apiResponseCallback);
@POST("/other")
public void test2(Callback<MyResponse<Product>> apiResponseCallback);
Problem 问题
I can access status
and message
fields from inside callback. 我可以从回调内部访问
status
和message
字段。 But information inside data
key is not parsed and model like MeUser
and Product
always returns as empty. 但是,
data
键内的data
未被解析,而像MeUser
和Product
这样的模型总是返回为空。
If I change json structure to following above code works perfectly - 如果我将json结构更改为以下代码完美无缺 -
{
"status": true,
"message": "Registration Complete.",
"data": {
"username": "user88",
"email": "user@domain.com",
"created_on": "1426171225",
"last_login": null,
"active": "1",
"first_name": "User",
"last_name": "",
"company": null,
"phone": null,
"sign_up_mode": "GOOGLE_PLUS"
}
}
How can I have it worked with specifying separate key inside data
object and parse it successfully ? 如何通过指定单独的键内部
data
对象并成功解析它?
If I can suggest to change something in json is that you have to add at one new field that defines the type of data, so json should look like below: 如果我可以建议在json中更改某些内容,则必须在一个定义数据类型的新字段中添加,因此json应如下所示:
{
"status": true,
"message": "Registration Complete.",
"dataType" : "user",
"data": {
"username": "user88",
"email": "user@domain.com",
"created_on": "1426171225",
"last_login": null,
"active": "1",
"first_name": "User",
"last_name": "",
"company": null,
"phone": null,
"sign_up_mode": "GOOGLE_PLUS"
}
}
The MyResponse
class has to have new filed DataType
so it should look like below: MyResponse
类必须具有新的DataType
因此它应如下所示:
public class MyResponse<T> implements Serializable{
private boolean status ;
private String message ;
private DataType dataType ;
private T data;
public DataType getDataType() {
return dataType;
}
//... other getters and setters
}
The DataType
is an enum which defines type of data. DataType
是一个定义数据类型的枚举。 You have to pass Data.class as param in constructor. 您必须在构造函数中将Data.class作为param传递。 For all data types you have to create new classes.
对于所有数据类型,您必须创建新类。
DataType
enum should look like below: DataType
枚举应如下所示:
public enum DataType {
@SerializedName("user")
USER(MeUser.class),
@SerializedName("product")
Product(Product.class),
//other types in the same way, the important think is that
//the SerializedName value should be the same as dataType value from json
;
Type type;
DataType(Type type) {
this.type = type;
}
public Type getType(){
return type;
}
}
The desarializator for Json should looks like below: Json的desarializator应如下所示:
public class DeserializerJson implements JsonDeserializer<MyResponse> {
@Override
public MyResponse deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
throws JsonParseException {
JsonObject content = je.getAsJsonObject();
MyResponse message = new Gson().fromJson(je, type);
JsonElement data = content.get("data");
message.setData(new Gson().fromJson(data, message.getDataType().getType()));
return message;
}
}
And when you create RestAdapter
, in the line where you register Deserializator, you should use this : 当您创建
RestAdapter
,在注册Deserializator的行中,您应该使用:
.registerTypeAdapter(MyResponse.class, new DeserializerJson())
Other classes (types of data) you define like standard POJO for Gson in separated classes. 您定义的其他类(数据类型),如分离类中的Gson标准POJO。
Your issue is because the data
attribute is defined as T
which you expect to be of types MeUser
, Product
, etc, but is actually of an object which has inner attribute like user
. 你的问题是,因为
data
属性定义为T
,你期望得到的类型MeUser
, Product
等,但实际上是一种具有像内属性的对象的user
。 To resolve this, you need to introduce another level of classes which has the required attributes user
, product
, invoice
etc. This can be easily achieved using static inner classes. 要解决此问题,您需要引入另一个级别的类,其中包含
user
, product
, invoice
等所需的属性。这可以使用静态内部类轻松实现。
public class MeUser{
private User user;
public static class User{
private String username;
//add other attributes of the User class
}
}
Might be a little bit off-topic, but what happens if the inner object contains a Date property? 可能有点偏离主题,但如果内部对象包含Date属性会发生什么? My TypeAdapter looks like this:
我的TypeAdapter看起来像这样:
.registerTypeAdapter(Date.class, new DateDeserializer())
.registerTypeAdapter(GenericNotificationResponse.class, new NotificationDeserializer())
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss")
.create();
But due to the parsing which is done by this : message.setData(new Gson().fromJson(data, message.getDataType().getType()));
但由于解析由此完成:
message.setData(new Gson().fromJson(data, message.getDataType().getType()));
It will throw an error whenever it will try to deserialize the Date property. 每当它尝试反序列化Date属性时,它都会抛出错误。 Is there a quick fix for this?
这是一个快速解决方案吗?
EDIT: Marked the answer as accepted, definitely :) it helped me fix my issue. 编辑:将答案标记为已接受,绝对:)它帮助我解决了我的问题。 But now there's this problem with the date deserializer.
但现在日期解串器出现了这个问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.