Eve
Eve

Reputation: 1238

SimpleXML and Retrofit: model for response with only root element

We are working on an Android project with an XML API, using RxJava, Retrofit and SimpleXML to handle requests and responses. The root of the responses can vary beteween two types:

<?xml version="1.0" encoding="UTF-8"?> 
<response>
 <Element1>Integer</Element1>
 <Element2>Integer</Element2>
 ...
</response>

or

<?xml version="1.0" encoding="UTF-8"?>
<error>
 <Element1>String</Element1>
 <Element2>Integer</Element2>
</error>

Therefore we use an XMLPullParse to parse the different kinds of responses to different models with their accompanying elements, which works fine.

MyResponse: The super class, which combines possible errors and valid responses:

public abstract class MyResponse<T> {
    public final MyError error;
    public final T data;

    protected MyResponse(MyError mError, T data) {
        this.error = mError;
        this.data = data;
    }

    public final boolean isError() {
        return error != null;
    }

    @Override
    public String toString() {
        return "MyResponse{" +
                "error=" + error +
                ", data=" + data +
                '}';
    }

    public T getData() {
        return data;
    }
}

SessionTokenResponse: Example for a response class which extends MyResponse

public class SessionTokenResponse extends MyResponse<SessionTokenResponseData>{
    public SessionTokenResponse(MyError mError, SessionTokenResponseData response) {
        super(mError, response);
}
}

SessionTokenResponseData: An example class showing how we build the model

@Root(name = "data")
public class SessionTokenResponseData {

    @Element(name = "Session")
    private String sessionInfo;

    @Element(name = "Token")
    private String tokenInfo;

    public String getSessionInfo() {
        return sessionInfo;
    }
    ....
}

RestClient - foo(): A method in our RestClient class to parse different kinds of responses. If it was successful, then the response will have <response> as root element, if not the root element will be of type <error>

private final <T extends MyResponse, I> Func1<ResponseBody, Observable<T>> foo(final Class<T> cls, final Class<I> innerCls) {
    return new Func1<ResponseBody, Observable<T>>() {
        @Override
        public Observable<T> 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((T) cls.getConstructor(MyError.class, innerCls).newInstance(myError, null));
                } else if (TextUtils.equals(rootTag, "response")) {
                    final I data = serializer.read(innerCls, xmlString);
                    return Observable.just((T) cls.getConstructor(HuiError.class, innerCls).newInstance(null, data));
                }

But we have problems with one type of response, which looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<response>
 OK
</response>

Our question would be: How can we build a model for this kind of response if it has no elements? We still need to be able to differentiate between other responses with a <response> root.

Upvotes: 2

Views: 925

Answers (1)

Eve
Eve

Reputation: 1238

After a spontaneous idea, we found the answer by ourselves. The response will extend the super class and will not contain any additional model class like my example class SessionTokenResponseData, but only a String:

public class LoginResponse extends MyResponse<String>{
    public LoginResponse(MyError mError, String response) {
        super(mError, response);
    }
}

So we don't need to modify our manual parsing in the RestClient class method and it works fine.

Upvotes: 1

Related Questions