Upgrade and SimpleXML for an unknown root element?

We are currently working on configuring Retrofit for the XML API - unfortunately each request can return a response with one of two different root elements .

Usually each answer looks like this (of course, the actual elements contained in the tag <response>differ with each request):

<?xml version="1.0" encoding="UTF-8"?>
<response>
    <SomeInfo>Some Info</SomeInfo>
    <MoreInfo>More Info</MoreInfo>
</response>

And each error looks like this (the structure here is the same for each answer):

<?xml version="1.0" encoding="UTF-8"?>
<error>
    <code>125002</code>
    <message></message>
</error>

Now, the only way we have found to get this work in a somewhat general way is the following:

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..."));
            }
        };
    }
โ€‹}

If the Response classes look like this:

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

and

public final class PinStatusResponse extends MyResponse<PinStatusResponseData> {
    public PinStatusResponse(MyError error, PinStatusResponseData data) {
        super(error, data);
    }
}

And all classes *Datacorrespond directly to XML responses (no errors).

, : ? ( , API?).

+4
1

@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;
}

* "ValidResponse" "ErrorReponse" XML. 'strict=false' @Root.

"Api", ( , 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 . .

0

Source: https://habr.com/ru/post/1618307/


All Articles