我们目前正在为XML API设置Retrofit - 遗憾的是每个请求都可以使用两个不同的根元素之一返回响应。
通常情况下,每个响应都是这样的(当然,<response>
标记中包含的实际元素因每个请求而异):
<?xml version="1.0" encoding="UTF-8"?>
<response>
<SomeInfo>Some Info</SomeInfo>
<MoreInfo>More Info</MoreInfo>
</response>
每个错误都是这样的(每个响应的结构都相同):
<?xml version="1.0" encoding="UTF-8"?>
<error>
<code>125002</code>
<message></message>
</error>
现在,到目前为止我们发现的唯一方式是以一种通用的方式完成这项工作:
public interface Api {
@GET("/api/sessionToken")
Observable<ResponseBody> requestSessionToken();
@GET("/api/pinStatus")
Observable<ResponseBody> requestPinStatus();
}
public class RestClient {
public RestClient() {
// ...
mApiService = retrofit.create(Api.class);
}
public Observable<PinStatusResponse> requestPinStatus() {
return mApiService.requestPinStatus()
.flatMap(foo(PinStatusResponse.class, PinStatusResponseData.class));
}
public Observable<SessionTokenResponse> requestSessionToken() {
return mApiService.requestSessionToken()
.flatMap(foo(SessionTokenResponse.class, SessionTokenResponseData.class));
}
private final <O extends MyResponse, I> Func1<ResponseBody, Observable<T>> foo(final Class<O> outerCls, final Class<I> innerCls) {
return new Func1<ResponseBody, Observable<O>>() {
@Override
public Observable<O> call(ResponseBody responseBody) {
try {
final String xmlString = responseBody.string();
final XmlPullParser parser = Xml.newPullParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
parser.setInput(new ByteArrayInputStream(xmlString.getBytes(Charset.forName("UTF-8"))), null);
parser.nextTag();
final String rootTag = parser.getName();
final Serializer serializer = new Persister();
if (TextUtils.equals(rootTag, "error")) {
final MyError myError = serializer.read(MyError.class, xmlString);
return Observable.just((O) outerCls.getConstructor(MyError.class, innerCls).newInstance(myError, null));
} else if (TextUtils.equals(rootTag, "response")) {
final I data = serializer.read(innerCls, xmlString);
return Observable.just((T) outerCls.getConstructor(MyError.class, innerCls).newInstance(null, data));
}
} catch (XmlPullParserException e) {
return Observable.error(e);
} catch (IOException e) {
return Observable.error(e);
} catch (Exception e) {
return Observable.error(e);
}
return Observable.error(new Exception("Should not be reached..."));
}
};
}
}
Response类看起来像这样:
public abstract class MyResponse<T> {
public final MyError error;
public final T data;
protected MyResponse(MyError error, T data) {
this.error = error;
this.data = data;
}
}
和
public final class PinStatusResponse extends MyResponse<PinStatusResponseData> {
public PinStatusResponse(MyError error, PinStatusResponseData data) {
super(error, data);
}
}
并且所有*Data
类都直接对应于(非错误)XML响应。
现在,我的问题是:是否真的没有更简单的方法来解决这个问题?(如果是这样,这是否是API设计错误的迹象?)。
答案 0 :(得分:0)
这是@ElementUnion
注释的用途。您可以通过使用带注释的对象使用纯Retrofit + SimpleXML API来解决这个问题:
@Root
public class ResponseBody {
public interface IApiResponse {
}
@Root
public static class ValidResponse implements IApiResponse {
@Element(name="SomeInfo") String someInfo;
@Element(name="MoreInfo") String moreInfo;
}
@Root
public static class ErrorResponse implements IApiResponse {
@Element(name="code") int code;
@Element(name="message") String message;
}
@ElementUnion({
@Element(name="response", type=ValidResponse.class),
@Element(name="error", type=ErrorResponse.class)
})
IApiResponse apiResponse;
}
*&#39; ValidResponse&#39;的具体结构和&#39; ErrorReponse&#39;必须根据您拥有的真实XML结构进行更改。您可能还希望考虑在'strict=false'
中添加@Root
。
至于你的Api&#39;界面,它必须看起来像这样(注意我已经在使用Retrofit的Call类中):
public interface Api {
@GET("/api/sessionToken")
Call<ResponseBody> requestSessionToken();
@GET("/api/pinStatus")
Call<ResponseBody> requestPinStatus();
}
最后,调用本身(例如requestPinStatus())应该根据这个骨架实现来解决:
Call<ResponseBody> result = mApiService.requestPinStatus();
result.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Response<ResponseBody> response, Retrofit retrofit) {
// ...
if (response.body().apiResponse instanceof ValidResponse) {
// ...
} else if (response.body().apiResponse instanceof ErrorResponse) {
// ...
}
}
@Override
public void onFailure(Throwable t) {
// ...
}
});
有关ElementUnion
和Simple-XML注释的详细信息,请参阅this guide。