September 11, 2012 07:31 by
Scott
In this blog post I will show how to implement a custom XmlMediaTypeFormatter that extends the default ASP.NET Web API XmlMediaTypeFormatter in a way that it ignores XML namespaces when parsing xml messages.
By default the ASP.NET Web API XmlMediaTypeFormatter is not able to parse XML requests that contain any XML namespace declarations. If you would like to support clients, that (for any reason) send messages containing XML namespaces you can use the IgnoreNamespacesXmlMediaTypeFormatter that is defined as follows:
public class IgnoreNamespacesXmlMediaTypeFormatter : XmlMediaTypeFormatter
{
// See http://wiki.tei-c.org/index.php/Remove-Namespaces.xsl
private const string NamespaceRemover =
@"<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:output method='xml' indent='no'/>
<xsl:template match='/|comment()|processing-instruction()'>
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match='*'>
<xsl:element name='{local-name()}'>
<xsl:apply-templates select='@*|node()'/>
</xsl:element>
</xsl:template>
<xsl:template match='@*'>
<xsl:attribute name='{local-name()}'>
<xsl:value-of select='.'/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>";
private readonly XslCompiledTransform _xlstTransformer;
public IgnoreNamespacesXmlMediaTypeFormatter()
{
var xslt = XDocument.Parse(NamespaceRemover, LoadOptions.PreserveWhitespace);
_xlstTransformer = new XslCompiledTransform();
_xlstTransformer.Load(xslt.CreateReader(), new XsltSettings(), new XmlUrlResolver());
}
public override Task<object> ReadFromStreamAsync(Type type, Stream stream, HttpContentHeaders contentHeaders, IFormatterLogger formatterLogger)
{
try
{
// Read XML
var xmlDocument = XDocument.Load(new XmlTextReader(stream));
// Transform XML
var resultStream = new MemoryStream();
_xlstTransformer.Transform(xmlDocument.CreateReader(), XmlWriter.Create(resultStream, new XmlWriterSettings() { OmitXmlDeclaration = true }));
resultStream.Position = 0;
// Process request with XmlMediaTypeFormatter default functionality
return base.ReadFromStreamAsync(type, resultStream, contentHeaders, formatterLogger);
}
catch (XmlException)
{
return base.ReadFromStreamAsync(type, stream, contentHeaders, formatterLogger);
}
}
}
In detail the IgnoreNamespacesXmlMediaTypeFormatter removes the XML namespace declarations from the XML message and passes the modified XML to the base class to use the default XmlMediaTypeFormatter functionality. Removing the XML namespaces is done with a XSLT transformation (see http://wiki.tei-c.org/index.php/Remove-Namespaces.xsl).
To activate the IgnoreNamespacesXmlMediaTypeFormatter add the following lines in the file Global.asax.cs:
protected void Application_Start()
{
[...]
// Remove default XmlFormatter and add (customized) IgnoreNamespacesXmlMediaTypeFormatter
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
var ignoreNamespacesXmlMediaTypeFormatter = new IgnoreNamespacesXmlMediaTypeFormatter{ UseXmlSerializer = true };
GlobalConfiguration.Configuration.Formatters.Add(ignoreNamespacesXmlMediaTypeFormatter);
[...]
}