Remove namespace in XML from ASP.NET Web API
Option 1 is to switch to using XmlSerializer
in GlobalConfiguration
:
config.Formatters.XmlFormatter.UseXmlSerializer = true;
Option 2 is to decorate your models with
[DataContract(Namespace="")]
(and if you do so, you'd need to decorate the members with [DataMember]
attributes).
If you're willing to decorate your model with XmlRoot, here's a nice way to do it. Suppose you have a car with doors. The default WebApi configuration will return something like :
<car
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<doors>
<door>
<color>black</color>
</door>
</doors>
</car>
This is what you want:
<car>
<doors>
<door>
<color>black</color>
</door>
</doors>
</car>
Here's the model:
[XmlRoot("car")]
public class Car
{
[XmlArray("doors"), XmlArrayItem("door")]
public Door[] Doors { get; set; }
}
What you have to do is create a custom XmlFormatter that will have an empty namespace if there are no namespaces defined in the XmlRoot attribute. For some reason, the default formatter always adds the two default namespaces.
public class CustomNamespaceXmlFormatter : XmlMediaTypeFormatter
{
public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content,
TransportContext transportContext)
{
try
{
var xns = new XmlSerializerNamespaces();
foreach (var attribute in type.GetCustomAttributes(true))
{
var xmlRootAttribute = attribute as XmlRootAttribute;
if (xmlRootAttribute != null)
{
xns.Add(string.Empty, xmlRootAttribute.Namespace);
}
}
if (xns.Count == 0)
{
xns.Add(string.Empty, string.Empty);
}
var task = Task.Factory.StartNew(() =>
{
var serializer = new XmlSerializer(type);
serializer.Serialize(writeStream, value, xns);
});
return task;
}
catch (Exception)
{
return base.WriteToStreamAsync(type, value, writeStream, content, transportContext);
}
}
}
Last thing to do is add the new formatter in the WebApiContext. Be sure to remove (or clear) the old XmlMediaTypeFormatter
public static class WebApiContext
{
public static void Register(HttpConfiguration config)
{
...
config.Formatters.Clear();
config.Formatters.Add(new CustomNamespaceXmlFormatter{UseXmlSerializer=true});
...
}
}
I like pobed2's answer. But I needed the CustomNamespaceXmlFormatter
to allow me to specify a default root namespace to be used when the XmlRoot
attribute is missing and also when it is present and has no value in the Namespace
property (that is, the attribute is used to set the root element name only). So I created an improved version, here it is in case it's useful for someone:
public class CustomNamespaceXmlFormatter : XmlMediaTypeFormatter
{
private readonly string defaultRootNamespace;
public CustomNamespaceXmlFormatter() : this(string.Empty)
{
}
public CustomNamespaceXmlFormatter(string defaultRootNamespace)
{
this.defaultRootNamespace = defaultRootNamespace;
}
public override Task WriteToStreamAsync(
Type type,
object value,
Stream writeStream,
HttpContent content,
TransportContext transportContext)
{
var xmlRootAttribute = type.GetCustomAttribute<XmlRootAttribute>(true);
if(xmlRootAttribute == null)
xmlRootAttribute = new XmlRootAttribute(type.Name)
{
Namespace = defaultRootNamespace
};
else if(xmlRootAttribute.Namespace == null)
xmlRootAttribute = new XmlRootAttribute(xmlRootAttribute.ElementName)
{
Namespace = defaultRootNamespace
};
var xns = new XmlSerializerNamespaces();
xns.Add(string.Empty, xmlRootAttribute.Namespace);
return Task.Factory.StartNew(() =>
{
var serializer = new XmlSerializer(type, xmlRootAttribute);
serializer.Serialize(writeStream, value, xns);
});
}
}
In the project that keeps response models go to Properties/AssemblyInfo.cs
Add
using System.Runtime.Serialization;
and at the bottom add
[assembly: ContractNamespace("", ClrNamespace = "Project.YourResponseModels")]
Replace Project.YourResponseModels
with the actual namespace where response models are located.
You need to add one per namespace