Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to pass 2 types of objects to Restsharp?

We have scenario where external API returns either User XML or Error XML based on whether request succeed or failed.

At the moment I'm passing User POCO to the restsharp and works fine. But if it fails, this object is NULL. And we won't know why it failed unless we parse the Error XML manually.

Is there a way to workaround this?

e.g.

var restClient = new RestClient(baseURL);
var request = new RestRequest(uri);
request.Method = Method.POST;
var response = restClient.Execute<User>(request);

On execution of above method the API can return Error xml object. How do I get Error object on fail and User on success?

like image 946
Nil Pun Avatar asked Apr 23 '15 00:04

Nil Pun


1 Answers

This is possible, although the code is a little ugly. RestSharp allows you to specify your own XML deserializer, so we'll need to do that in order to make this work.

First, though, you need a data type that can store either an Error or a User (I made it generic so it works for more than just Users):

public class Result<T>
{
    public T Data { get; set; }
    public Error Error { get; set; }
}

So the idea is, now when you execute the request, you ask RestSharp for a Result<User> instead of just a User, i.e.:

var result = client.Execute<Result<User>>(request);

Now here's the magic required to deserialize as either an Error or a User. It's a custom deserializer that inherits from RestSharp's XmlDeserializer. Warning: this code is not tested at all, but it can hopefully point you in the right direction.

public class XmlResultDeserializer : XmlDeserializer
{
    public override T Deserialize<T>(IRestResponse response)
    {
        if (!typeof(T).IsGenericType || typeof(T).GetGenericTypeDefinition() != typeof(Result<>))
            return base.Deserialize<T>(response);

        // Determine whether the response contains an error or normal data.
        var doc = XDocument.Parse(response.Content);
        var result = Activator.CreateInstance<T>();
        if (doc.Root != null && doc.Root.Name == "Error")
        {
            // It's an error
            var error = base.Deserialize<Error>(response);
            var errorProperty = result.GetType().GetProperty("Error");
            errorProperty.SetValue(result, error);
        }
        else
        {
            // It's just normal data
            var innerType = typeof(T).GetGenericArguments()[0];
            var deserializeMethod = typeof(XmlDeserializer)
                .GetMethod("Deserialize", new[] { typeof(IRestResponse) })
                .MakeGenericMethod(innerType);
            var data = deserializeMethod.Invoke(this, new object[] { response });

            var dataProperty = result.GetType().GetProperty("Data");
            dataProperty.SetValue(result, data);
        }
        return result;
    }
}

Then you would wire it all up like this:

var restClient = new RestClient(baseURL);
client.AddHandler("application/xml", new XmlResultDeserializer());
var request = new RestRequest(uri);
request.Method = Method.POST;
var result = restClient.Execute<Result<User>>(request);
if (response.Data.Data != null)
{
    var user = response.Data.Data;
    // Do something with the user...
}
else if (response.Data.Error != null)
{
    var error = response.Data.Error;
    // Handle error...
}
like image 83
AJ Richardson Avatar answered Sep 23 '22 08:09

AJ Richardson