java 为具有不同对象的嵌套 json 改造 gson 转换器
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29026596/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
retrofit gson converter for nested json with different objects
提问by Gimali
I've JSON structure like follows -
我的 JSON 结构如下 -
{
"status": true,
"message": "Registration Complete.",
"data": {
"user": {
"username": "user88",
"email": "[email protected]",
"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. data
will be treated according to status
and message
will be displayed to user.
我想在每个休息响应中保持status
,message
和data
键相同。data
将被处理status
并message
显示给用户。
So basically, above format is desired in all apis. Only information inside data
key will be different each time.
所以基本上,所有 api 都需要上述格式。只有data
key里面的信息每次都会不同。
And I've setup a following class and set it up as gson converter - MyResponse.java
我已经设置了以下类并将其设置为 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
解串器.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 interfaceis 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. But information inside data
key is not parsed and model like MeUser
and Product
always returns as empty.
我可以从内部回调访问status
和message
字段。但是data
key 中的信息没有被解析和模型一样,MeUser
并且Product
总是返回为空。
If I change json structure to following above code works perfectly -
如果我将 json 结构更改为以下代码,则可以完美运行 -
{
"status": true,
"message": "Registration Complete.",
"data": {
"username": "user88",
"email": "[email protected]",
"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
对象内指定单独的键并成功解析它?
回答by Konrad Krakowiak
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": "[email protected]",
"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. You have to pass Data.class as param in constructor. For all data types you have to create new classes. DataType
enum should look like below:
的DataType
是定义数据类型枚举。您必须在构造函数中将 Data.class 作为参数传递。对于所有数据类型,您必须创建新类。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 的反序列化器应如下所示:
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
,在你注册反序列化器的那一行,你应该使用这个:
.registerTypeAdapter(MyResponse.class, new DeserializerJson())
Other classes (types of data) you define like standard POJO for Gson in separated classes.
您在分离的类中定义的其他类(数据类型)就像 Gson 的标准 POJO。
回答by Rajesh
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
. 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.
你的问题是因为data
属性定义为T
你期望得到的类型MeUser
,Product
等等,但实际上是一种具有类似属性内的对象user
。为了解决这个问题,就需要引入具有所需属性的类另一个层面user
,product
,invoice
等,这可以很容易地使用静态内部类实现。
public class MeUser{
private User user;
public static class User{
private String username;
//add other attributes of the User class
}
}
回答by Alex
Might be a little bit off-topic, but what happens if the inner object contains a Date property? My TypeAdapter looks like this:
可能有点跑题,但是如果内部对象包含 Date 属性会发生什么?我的 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. Is there a quick fix for this?
每当它尝试反序列化 Date 属性时,它都会抛出错误。有没有快速解决这个问题的方法?
EDIT: Marked the answer as accepted, definitely :) it helped me fix my issue. But now there's this problem with the date deserializer.
编辑:将答案标记为已接受,绝对是:) 它帮助我解决了我的问题。但是现在日期反序列化器存在这个问题。