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

时间:2015-03-13 07:04:07

标签: java android json gson retrofit

我的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"
        }
    }
}

以上格式很常见。只有data密钥才能包含不同类型的信息,例如userproductinvoice等。

我希望在每个休息响应中保持statusmessagedata个键相同。我会根据data处理status,并向用户显示message

所以基本上,所有api都需要以上格式。 每次data键内的信息都不同。

我已经设置了以下课程并将其设置为gson转换器 - 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

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

    }
}

并按如下方式使用 -

GsonBuilder  gsonBuilder = new GsonBuilder();
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES); 
gsonBuilder.registerTypeAdapter(MyResponse.class, new Deserializer<MyResponse>());
...... ..... ....

restBuilder.setConverter(new GsonConverter(gsonBuilder.create()));

服务界面如下 -

@POST("/register")
public void test1(@Body MeUser meUser, Callback<MyResponse<MeUser>> apiResponseCallback);


@POST("/other")
public void test2(Callback<MyResponse<Product>> apiResponseCallback);

问题

我可以从回调内部访问statusmessage字段。但是data键中的信息未被解析,而像MeUserProduct这样的模型总是返回为空。

如果我将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"
        }
    }

如何在data对象中指定单独的键并成功解析它?

3 个答案:

答案 0 :(得分:24)

如果我可以建议在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"
    }
}

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
}

DataType是一个定义数据类型的枚举。您必须在构造函数中将Data.class作为param传递。对于所有数据类型,您必须创建新类。 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;
    }
}

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;

    }
}

当你在注册Deserializator的行中创建RestAdapter时,你应该使用它:

 .registerTypeAdapter(MyResponse.class, new DeserializerJson())

您定义的其他类(数据类型),如分离类中Gson的标准POJO。

答案 1 :(得分:1)

您的问题是因为data属性定义为T,您希望它属于MeUserProduct等类型,但实际上是一个对象有内部属性,如user。要解决此问题,您需要引入具有所需属性userproductinvoice等的其他级别的类。这可以使用静态内部类轻松实现。

public class MeUser{
  private User user;
  public static class User{
    private String username;
    //add other attributes of the User class
  }
}

答案 2 :(得分:-1)

可能有点偏离主题,但如果内部对象包含Date属性会发生什么?我的TypeAdapter看起来像这样:

 .registerTypeAdapter(Date.class, new DateDeserializer())
                .registerTypeAdapter(GenericNotificationResponse.class, new NotificationDeserializer())
                .setDateFormat("yyyy-MM-dd'T'HH:mm:ss")
                .create();

但由于解析由此完成:message.setData(new Gson().fromJson(data, message.getDataType().getType()));

每当尝试反序列化Date属性时,都会抛出错误。对此有快速解决方法吗?

编辑:将答案标记为已接受,绝对是:)它帮助我解决了我的问题。但是现在日期反序列化器存在这个问题。