简体   繁体   English

改造用于嵌套json的gson转换器与不同的对象

[英]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键可以容纳不同类型的信息,如userproductinvoice等。

I want to keep status , message and data keys same in every rest response. 我想在每个休息响应中保持statusmessagedata键相同。 data will be treated according to status and message will be displayed to user. data将根据待治疗statusmessage将被显示给用户。

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. 我可以从回调内部访问statusmessage字段。 But information inside data key is not parsed and model like MeUser and Product always returns as empty. 但是, data键内的data未被解析,而像MeUserProduct这样的模型总是返回为空。

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 ,你期望得到的类型MeUserProduct等,但实际上是一种具有像内属性的对象的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. 要解决此问题,您需要引入另一个级别的类,其中包含userproductinvoice等所需的属性。这可以使用静态内部类轻松实现。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM