AspNet Core and Xml
Tags: dotnet, asp, aspnet
Published: 2021-05-14 13:56:56

I have been recently working on a legacy api that accepts xml that uses namespaces, and I found the documentation in this space very sparse. So I'm hoping that if I ever have this problem again I can just search for this blog post and read what I wrote 😂

So let's talk about xml:

## Accepting xml in asp .net core 

First of all there is a small piece of code that we need to add to our startup to accept xml in our api:

```c#
public void ConfigureServices(IServiceCollection services)
{
    //
    services.AddControllers()
        .AddXmlSerializerFormatters();
    ///
}
```

This adds the default input and output formatters - which trust me we'll come back to!

If we haev OpenApi connected to our application then we need to tell it that we accept xml:

```c#
[ApiController]
public class WeatherController : BaseController
{
    [Produces("application/xml"), Consumes("application/xml")]
    public IActionResult Test()
    {

    }
}
```

Don't forget to add the correct tag to tell asp where your going to get your information from:
```c#
[Produces("application/xml"), Consumes("application/xml")]
public IActionResult Test([FromBody] RequsetModel request)
{

}
```

## Namespaces

When I was looking into this problem this was probably the _biggest_ headache, searching for how to actually add a namespace onto a xml is a nightmare, lots of false information laying around so...... Firstly we want to "declare" which namespaces we want to use, this can go in any class, I opted to create a namespace class:

```c#
public static class XmlNamespaces
{
    [XmlNamespaceDeclarations]
    public static XmlSerializerNamespaces Namespaces 
    {
        get
        {
            return new XmlSerializerNamespaces(new[]
            {
                new XmlQualifiedName("", "http://schemas.xmlsoap.org/soap/envelope/"),
                new XmlQualifiedName("xsi", "http://www.w3.org/2001/XMLSchema-instance")
            });
        }
    }
}
```

Please ignore the fact that I'm adding soap envelope namespace, that's just an example 💀 I swear.

Now that we have the namespaces declared we can now add them to our model:

```c#


[XmlRoot("Envelope", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public class RequsetModel 
{
    [XmlElement(Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
    public Header? Header { get; set; }
    
    [XmlElement(Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
    public BulkActionBody Body { get; set; }
}
```

now if we were to request the api we should be able to send a request that looks like:

```xml
<Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <xsi:Header></xsi:Header/>
    <xsi:Body></xsi:Body/>
</Envelope>
```

## Outputting xml 

You know how I said before that we would come back to the default serializers in aspnet core? Well here we go! Turns out declaring these namespaces only works for requests, if we were to return a model that had these attributes on it (with namespaces that were declared) it won't actually add those namespaces.

First we need to override our serializer:

```c#

public class XmlOutputSerializer : XmlSerializerOutputFormatter
{
    protected override void Serialize(XmlSerializer xmlSerializer, XmlWriter xmlWriter, object value)
    {
        xmlSerializer.Serialize(xmlWriter, value, XmlNamespaces.Namespaces);
    }
}
```

This tells the serializer that we want to use the namespaces that we have declared when deserializing xml.

Next we need to tell Asp net core that we want to use this serilizer and not the default one, back to our startup:


```c#
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options => options.OutputFormatters.Add(new XmlOutputSerializer()))
        .AddXmlSerializerFormatters();
}

```

And kapow 🎉🎊🎊🎉 There's three weeks worth of googling in a 2 minute blog post