Refit: Support BodySerializationType.Xml

Created on 24 Nov 2014  ·  20Comments  ·  Source: reactiveui/refit

Why only Json and Forms? Xml is still needed for many legacy interfaces.

outdated

Most helpful comment

The +1s predate the 👍 reaction. They're not needed anymore to signal approval.

All 20 comments

Purely because nobody's requested it yet and we haven't had use for it, I think. I'm happy to implement it if you need it. :smile:

I am thinking if there is a real need for it or I cloud just live with body string and serialize myself.

Hmm, _what I'm about to suggest isn't possible right now_, but we could look at adding support for BodySerializationMethod.String or BodySerializationMethod.Custom or something to make the following possible:

public ISomeApi
{
    [Post("/something")]
    Task PostSomeXml([Body(BodySerializationMethod.String)]Xml<SomeType> payload);
}

public class Xml<T>
{
    static XmlSerializer Serializer = new XmlSerializer(typeof(T));

    T value;

    public override string ToString()
    {
        // You could really do any string serialization you want here.
        using(var writer = new StringWriter())
        {
            Serializer.Serialize(writer, value);
            return writer.ToString();
        }
    }

    public static Xml<T> Parse(string xml)
    {
        using(var reader = new StringReader(xml))
        {
            var value = (T)Serializer.Deserialize(reader);
            return new Xml<T>{ value = value };
        }
    }

    public static implicit operator Xml<T>(T value)
    {
        return value != null 
            ? new Xml<T>{ value = value } 
            : null;
    }

   public static implicit operator T(Xml<T> wrapper)
   {
       return wrapper != null
              ? wrapper.value
            : null;  
   }
}

// Then in the calling code
RestService.For<ISomeApi>("http://somewhere.com").PostSomeXml(new SomeType { /* whatever */ });

Then you could implement any custom serialization you want and wouldn't need to monkey about with manually serializing to/from string for each call, because that's super boring boilerplate stuff.

That only covers the _request_ body though so we would probably also need to adjust the deserialization on the way out so you could specify something like this (which would need a static Parse method to be declared on your custom type):

public interface ISomeApi
{
    [Get("/something")]
    [return: Body(BodySerializationMethod.String)]
    Task<Xml<SomeType>> GetSomeXml();
}

It might also be a good idea to look at supporting for the [Body] attribute at a method level as well as on a property, so you could just do this (instead of having to mark up both the return and a parameter with a body attribute):

public interface ISomeApi
{
    [Put("/something")]
    [Body(BodySerializationMethod.String)]
    Task<Xml<SomeResponseType>> PutSomeXml(Xml<SomeRequestType> payload);
}

I feel like an approach like this would give people flexibility to do whatever they want _without requiring them to wrap every call to the API to serialize/deserialize the request/response_. We could even implement basic XML serialization like the above class as an example.

You reckon this is a good idea, @paulcbetts?

I think it is a great idea, I was thinking about Custom method but my thoughts were more around reflections but your implementation with generic looks much better.

So, instead of doing a parameterized type like this, I'd rather start to recognize custom serializers by type, something like:

interface ITypeSerializer : {
    Task<HttpContent> SerializeAsHttpContent(object value);
    Task<object> DeserializeFromHttpContent(HttpContent content);
}

Then later, have a dictionary we'd look up on inside RestService (or really, its implementation)`

Dictionary<Type, ITypeSerializer> customTypeSerializers;

Then, you'd register a serializer for SomeResponseType and handle the XML yourself (we could optionally provide some good base classes to help out with this).

The moral of the story is though, that this is an edge case, Developer Must Suffer scenario - only developers that _have this problem_ should endure the suffering; developers who are doing things the Right Way shouldn't feel the paper cuts of accommodating this scenario

What would be "the right way" if I have to post to some external system that only accepts Xml?

Sent from my Windows Phone


From: Paul Bettsmailto:[email protected]
Sent: ‎24/‎11/‎2014 23:00
To: paulcbetts/refitmailto:[email protected]
Cc: Alexey Zimarevmailto:[email protected]
Subject: Re: [refit] Support BodySerializationType.Xml (#75)

So, instead of doing a parameterized type like this, I'd rather start to recognize custom serializers by type, something like:

interface ITypeSerializer : {
    Task<HttpContent> SerializeAsHttpContent(object value);
    Task<object> DeserializeFromHttpContent(HttpContent content);
}

Dictionary customTypeSerializers;

Then, you'd register a serializer for `SomeResponseType` and handle the XML yourself (we could optionally provide some good base classes to help out with this).

The moral of the story is though, that this is an edge case, Developer Must Suffer scenario - only developers that *have this problem* should endure the suffering; developers who are doing things the Right Way shouldn't feel the paper cuts of accommodating this scenario

---
Reply to this email directly or view it on GitHub:
https://github.com/paulcbetts/refit/issues/75#issuecomment-64272672

Yeah, I was thinking after I wrote that that maybe formalizing the formatter into an interface would be better.

Would you still wire it up via an attribute? Something like [Body(typeof(MyTypeSerializer))]?

Would you still wire it up via an attribute? Something like [Body(typeof(MyTypeSerializer))]?

No, you'd set it up in the constructor of RestService.For - basically I want to avoid too much noise being attached to attributes

What would be "the right way" if I have to post to some external system that only accepts Xml?

I mean "the right way" in that, XML services are far less common and are a relic of an earlier time - I still want to make it work for those who want to use them, _but_ I don't want to ruin the experience for people writing newer software. Does that make sense? We can make both work, but I want to prefer newer paradigms over older ones.

No, you'd set it up in the constructor of RestService.For - basically I want to avoid too much noise being attached to attributes

:+1:

How would someone indicate that the serializer should be used for a particular method? Would we just use it everywhere there was a [Body] attribute? If so, isn't that a bit confusing given that [Body] is implicitly JSON?

Or would we instead just use the serializer for every request that carries content body (i.e. POST/PUSH) for the first parameter that isn't defined in the URL format string?

At the end I believe that custom serializers is a better idea since it generalize the issue and allows "everything else" to be done. Although I don't exactly agree about XML, I see the point, especially considering the fact that XML serializers might have different flavours that are not easy to support by a standard serializer, so there will be deviations anyway.

Just adding my 2cents here: a generic approach in refit supporting 'any' typeof body (de)serializer (protobuf, messagepack, etc) or different Json deserializers (Jil / NetJson / ServiceStack etc) would be neat (see my original #106 ).

As a refit user I could live with both, per-method attribute or a 'global' hook that changes every body de/serialization globally. However, I haven't encountered APIs that have different serialization formats for their endpoints, however there might be some of those, too (thinking about versioned/legacy APIs).

But maybe a global switch/setting that sets the default and an opt-in override attribute for specific methods that deviate from the global one would be nice

Implicitly using Json and Json.Net is an understandable default choice, but from a generic point of view it just ain't right.

NancyFx had a similar issue a while back and what they did is basically pull in mono's Json (de)serializer into the framework itself and have it as the default one, Json.Net and a bunch of others are separate packages which the developer can choose from. NanyFx also provides both, global and/or method-specific serialization: developers choose which serializers they want to support/include and depending on content-type of a request the right one, if one is available, is chosen.. or, if it is required in a method, one can bypass the content-type negotiation process and return the response including the body directly.

Not saying it is the right way to do it or one refit "should" do, I can just speak from my own experience that this approach did feel quite natural and understandable when starting to work with Nancy.

+1

+1

+1

+1

+1

+1

The +1s predate the 👍 reaction. They're not needed anymore to signal approval.

I'm not sure exactly when it was added, but XML is supported in request and response bodies now.

Docs here: https://github.com/reactiveui/refit/blob/master/README.md#xml-content

GitHub
The automatic type-safe REST library for Xamarin and .NET - reactiveui/refit
Was this page helpful?
0 / 5 - 0 ratings

Related issues

tstivers1990 picture tstivers1990  ·  5Comments

sahinyyurt picture sahinyyurt  ·  5Comments

hiraldesai picture hiraldesai  ·  4Comments

ahmedalejo picture ahmedalejo  ·  6Comments

SOFSPEEL picture SOFSPEEL  ·  4Comments