Adjust MVC 4 WebApi XmlSerializer to lose the nameSpace
可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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 accept xml objects without it. (json works just fine)
22testas@email.com ...
I would like this to work
22testas@email.com ...
Hopefully without having to decorate the POCO's with a bunch of attributes.
I've set up a test solution for this, and indeed, these methods are beeing hit (must be some other problem in my system). Anyways - the result that I get using this solutions is this:
22TestAccTest@Test.com
Got rid of the schema on top, but the properties are now messed up :( Here's a link to a sample project
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;
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.
回答3:
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 { 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.
回答4:
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 NotificationXmlFormatter() { 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