I\'m working on a MVC WebAPI, that uses EF with POCO classes for storage. What I want to do is get rid of the namespace from the XML, so that the endpoints would return and
I have customized Boris's answer to MVC Webapi 5. Use either of the following http headers render the result using the CustomFormatter:
accept: application/xml
accept: text/xml
WebApiConfig.cs :
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
GlobalConfiguration.Configuration.Formatters.Add(new CustomXmlFormatter());
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
}
}
CustomXmlFormatter.cs :
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Xml.Serialization;
namespace Custom.Formatter
{
public class CustomXmlFormatter: MediaTypeFormatter
{
private UTF8Encoding encoder;
public CustomXmlFormatter()
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/xml"));
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/xml"));
encoder = new UTF8Encoding(false, true);
}
public override bool CanReadType(Type type)
{
if (type == (Type)null)
throw new ArgumentNullException("type");
//Type filtering
if (type == typeof(SendEmailMessageResponse) || type == typeof(SendSmsMessageResponse))
return true;
else
return false;
}
public override bool CanWriteType(Type type)
{
return true;
}
public override Task<object> ReadFromStreamAsync(Type type, Stream stream, HttpContent content, IFormatterLogger formatterLogger)
{
return Task.Factory.StartNew(() =>
{
using (var streamReader = new StreamReader(stream, encoder))
{
var serializer = new XmlSerializer(type);
return serializer.Deserialize(streamReader);
}
});
}
public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContent content, TransportContext transportContext)
{
var serializer = new XmlSerializer(type);
return Task.Factory.StartNew(() =>
{
using (var streamWriter = new StreamWriter(stream, encoder))
{
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
serializer.Serialize(streamWriter, value, ns);
}
});
}
}
}
It's been awhile since I messed with MVC 4, but we ended up replacing the default formatter with the XmlSerializer like so:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings = GetSerializeSettings();
GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true;
}
internal JsonSerializerSettings GetSerializeSettings()
{
return new JsonSerializerSettings
{
Formatting = Formatting.Indented,
ContractResolver = new CamelCasePropertyNamesContractResolver(),
Converters = new List<JsonConverter> { new IsoDateTimeConverter() }
};
}
This might help... I know we also customized the property names using attributes on the POCOs which you said you don't want to do, but that's because we wanted them to be camel-cased.
This answer here is spot on the mark Remove namespace in XML from ASP.NET Web API.\
If you don't want to decorate your POCO's at all use the 1st option:
config.Formatters.XmlFormatter.UseXmlSerializer = true;
If you use option 2, you may need to add a reference to System.Runtime.Serialization
Assuming a post like this with Accept set correct:
GET http:// ANY OLD SERVER/api/foos/5 Accept: application/xml
Controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Runtime.Serialization;
using System.Web.Http;
namespace CutomXmlFormater.Controllers
{
//[DataContract(Namespace = "")]
public class Foo
{
//[DataMember]
public string Bar { get; set; }
}
public class FoosController : ApiController
{
// GET api/foos/5
public Foo Get(int id)
{
return new Foo() { Bar = "Test" };
}
}
}
Config (App_Start/WebApiConfig)
//(Use this is you don't go the data contact and model annotation route)
config.Formatters.XmlFormatter.UseXmlSerializer = true;
Result
Either (With annotation and data contact):
<Foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Bar>Test</Bar></Foo>
Or (with XML serialiser route):
<Foo xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Bar>Test</Bar></Foo>
Maybe you could try with this:
Replace default XmlFormatter with your own:
GlobalConfiguration.Configuration.Formatters.Add(new CustomXmlFormatter());
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
And impement it using XmlSerializer, with specifying empty namespace during serialization, like this:
public CustomXmlFormatter()
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/xml"));
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/xml"));
Encoding = new UTF8Encoding(false, true);
}
protected override bool CanReadType(Type type)
{
if (type == (Type)null)
throw new ArgumentNullException("type");
if (type == typeof(IKeyValueModel))
return false;
return true;
}
protected override bool CanWriteType(Type type)
{
return true;
}
protected override Task OnReadFromStreamAsync(Type type, Stream stream, HttpContentHeaders contentHeaders, FormatterContext formatterContext)
{
return Task.Factory.StartNew(() =>
{
using (var streamReader = new StreamReader(stream, Encoding))
{
var serializer = new XmlSerializer(type);
return serializer.Deserialize(streamReader);
}
});
}
protected override Task OnWriteToStreamAsync(Type type, object value, Stream stream, HttpContentHeaders contentHeaders, FormatterContext formatterContext, System.Net.TransportContext transportContext)
{
var serializer = new XmlSerializer(type);
return Task.Factory.StartNew(() =>
{
using (var streamWriter = new StreamWriter(stream, Encoding))
{
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
serializer.Serialize(streamWriter, value, ns);
}
});
}
}
Custom XML serializer was stolen from here, and as such is untested.
This should serialize objects w/o writing the namespace. I'm not sure if it will work OOTB for deserialization, you'd may have to experiment with XmlSerializer.Deserialize()
overload that provides events and handle UnknownElement or UnknownNode event.