使用GSON修改改造响应 [英] Adapting Retrofit responses using GSON

查看:81
本文介绍了使用GSON修改改造响应的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



每个服务器响应都会返回以下JSON。

每个服务器响应都会返回以下JSON格式(简明扼要):

  {
status:success,
error_title :,
error_message:,
data:{
messages:[
{message_id:123,
content:这是一条消息},
{message_id:124,
content:这是另一条消息}
]
}
}

错误响应包含与data对象相同的一般格式为空以及与错误相关的包含有用值的JSON对象。在发生错误的情况下,我想提取与错误相关的JSON对象。



通过上述响应,我有一个 MessageResponse 包含status,errorTitle和errorMessage字符串属性的类以及 MessageData 对象。然后, MessageData 对象包含一个消息列表 - List< Message>消息。我在这种情况下获取消息的GET方法如下(简洁起见):

  @GET(/ chat / conversation )
void getMessages(Callback< MessageResponse> callback);

如果我坚持使用每个响应类型,到GSON串行器提供的默认POJO映射。我的最终目标是通过仅从阅读成功的服务器响应中读取我需要的内容并忽略其他内容,从而减少必需的类的数量。我想我所有的成功,这个API的回调数据类型尽可能接近数据内容。



换句话说,我想不会返回数据的子元素。在上面的例子中,它是一个名为messages的数组,但在其他一些响应中,它可能是一个user对象。我知道这可以通过为每个响应类型注册单独的 TypeAdapter s来完成,但我希望通过使用单个通用解决方案。



更新:从下面实施David的建议
$ b

  public class BaseResponse< T> {
@SerializedName(status)public String status;
@SerializedName(error_title)public String errorMessageTitle;
@SerializedName(error_message)public String errorMessage;
@SerializedName(data)public T data;
}

public class MessagesResponse extends BaseResponseData< List< Message>> {
@SerializedName(messages)List< Message>消息;
}

@GET(/ chat / conversation)
void getMessages(Callback< BaseResponse< MessageResponse>> callback);

不幸的是,这没有得到正确的序列化。如果我只能通过某种方式通知GSON从data父项中指定了一个可变名称的JSON对象子元素,并将该子元素反序列化为通用数据类型引用的模型类。基本上, dataJsonObject.getChild()

解决方案

向GSON提供通用的基础响应类失败后,我最终传递了这条路线,并解决了我前几天实施的解决方案(减去状态检查条件)。

GSON提供了通过在通用 TypeAdapterFactory 中定义反序列化逻辑来为所有响应添加 TypeAdapter 的功能。这个实体并不像我希望的那样干净和无知,但它在完成必要响应模型类的数量减少的同时还维护一个适配器。

  private static class ResponseTypeAdapterFactory implements TypeAdapterFactory {

private static final String STATUS =status;
private static final String SUCCESS =success;
private static final String DATA =data;

@Override
public< T> TypeAdapter< T>创建(Gson gson,TypeToken< T>类型){
final TypeAdapter< T> delegateAdapter = gson.getDelegateAdapter(this,type);
final TypeAdapter< JsonElement> jsonElementAdapter = gson.getAdapter(JsonElement.class);

返回新的TypeAdapter< T>(){
@Override
public void write(JsonWriter out,T value)throws IOException {
delegateAdapter.write(out,值);
}

@Override
public T read(JsonReader in)throws IOException {
//当响应为a时,忽略无关数据并仅读入响应数据成功
JsonElement jsonElement = jsonElementAdapter.read(in);
if(jsonElement.isJsonObject()){
JsonObject jsonObject = jsonElement.getAsJsonObject(); (jsonObject.has(STATUS)){
if(jsonObject.get(STATUS).getAsString()。equals(SUCCESS)){
if(jsonObject.has(DATA)& ;& jsonObject.get(DATA).isJsonObject()){
jsonElement = jsonObject.get(DATA);
}
}
}
}
return delegateAdapter.fromJsonTree(jsonElement);
}
} .nullSafe();


$ / code>

简而言之,我告诉GSON抓住如果响应成功,则返回数据JSON对象。否则,请返回整个响应主体,以便我的自定义Retrofit错误处理程序可以使用服务器返回的error_title和error_message字段。



向@ david.mihola大声建议,并最终将注意力转移回 TypeAdapterFactory 解决方案。


I would like to agnostically retrieve the child element of a known JSON object with every successful response I receive from a particular API.

Each server response returns the following JSON format (condensed for simplicity):

{
    "status": "success",
    "error_title": "",
    "error_message": "",
    "data": {
        "messages": [
            { "message_id": "123",
              "content": "This is a message" },
            { "message_id": "124",
              "content": "This is another message" }
        ]
    }
}

Error responses contain the same, general format, with the "data" object being empty and the error-related JSON objects containing useful values. In the case of an error, I would like to extract the error-related JSON objects.

With the above response, I have a MessageResponse class that contains status, errorTitle, and errorMessage String properties as well as a MessageData object. The MessageData object then contains a list of messages - List<Message> messages. My GET method for getting messages in this case is as follows (condensed for simplicity):

@GET("/chat/conversation")
void getMessages(Callback<MessageResponse> callback);

This design requires three classes for each response type if I were to stick to the default POJO mapping that GSON's serializer provides out-of-box. My end goal is to cut down on the amount of classes necessary by reading only what I need from a successful server response and ignoring the rest. I would like all my success, callback data types on this API to be as close to the "data" content as possible.

In other words, I would like to agnostically return the child element of "data". In the case above, it is an array called "messages", but in some other response it could be a "user" object, for example. I know this can be done by registering separate TypeAdapters for each response type, but I would like to achieve my end goal by using a single, generic solution.

UPDATE: Implementation of David's suggestion from below

public class BaseResponse<T> {
     @SerializedName("status") public String status;
     @SerializedName("error_title") public String errorMessageTitle;
     @SerializedName("error_message") public String errorMessage;
     @SerializedName("data") public T data;
}

public class MessagesResponse extends BaseResponseData<List<Message>> {
     @SerializedName("messages") List<Message> messages;
}

@GET("/chat/conversation")
void getMessages(Callback<BaseResponse<MessageResponse>> callback);

Unfortunately this is not getting serialized properly. If only I could somehow inform GSON of a variably-named JSON object child from the "data" parent and deserialize that child into a model class that is referred to by a generic data type. Essentially, dataJsonObject.getChild().

解决方案

After a few hours of unsuccessfully feeding generic, base response classes to GSON, I ended up passing on that route and settling on a solution that I implemented a few days ago (minus the status check conditionals).

GSON provides the ability to add a TypeAdapter to all responses by defining deserialization logic in a generic TypeAdapterFactory. This entity is not as clean and ignorant as I was hoping for it to be, but it does the job in achieving a reduction in the number of necessary response model classes while also maintaining a single adapter.

private static class ResponseTypeAdapterFactory implements TypeAdapterFactory {

    private static final String STATUS = "status";
    private static final String SUCCESS = "success";
    private static final String DATA = "data";

    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        final TypeAdapter<T> delegateAdapter = gson.getDelegateAdapter(this, type);
        final TypeAdapter<JsonElement> jsonElementAdapter = gson.getAdapter(JsonElement.class);

        return new TypeAdapter<T>() {
            @Override
            public void write(JsonWriter out, T value) throws IOException {
                delegateAdapter.write(out, value);
            }

            @Override
            public T read(JsonReader in) throws IOException {
                // Ignore extraneous data and read in only the response data when the response is a success
                JsonElement jsonElement = jsonElementAdapter.read(in);
                if (jsonElement.isJsonObject()) {
                    JsonObject jsonObject = jsonElement.getAsJsonObject();
                    if (jsonObject.has(STATUS)) {
                        if (jsonObject.get(STATUS).getAsString().equals(SUCCESS)) {
                            if (jsonObject.has(DATA) && jsonObject.get(DATA).isJsonObject()) {
                                jsonElement = jsonObject.get(DATA);
                            }
                        }
                    }
                }
                return delegateAdapter.fromJsonTree(jsonElement);
            }
        }.nullSafe();
    }
}

In a nutshell, I'm telling GSON to grab the "data" JSON object if the response was successful. Otherwise, return the entire response body so that my custom, Retrofit error handler can make use of the "error_title" and "error_message" fields returned from the server.

A huge shoutout to @david.mihola for the great suggestions and eventually directing my attention back to the TypeAdapterFactory solution.

这篇关于使用GSON修改改造响应的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆