XML Namespaces in ASP.NET Web API

南楼画角 提交于 2019-11-29 21:17:45

问题


I am currently working on a project that requires me to output XML from its endpoints along with JSON. I have the following model:

[DataContract(Namespace="http://www.yale.edu/tp/cas")]
[XmlType("serviceResponse")]
[XmlRoot(Namespace="http://www.yale.edu/tp/cas")]
public class ServiceResponse
{
    [XmlElement("authenticationSuccess")]
    public AuthenticationSuccess Success { get; set; }

    [XmlElement("authenticationFailure")]
    public AuthenticationFailure Failure { get; set; }
}

The output is as follows when success is not null:

<serviceResponse>
<authenticationSuccess />
</serviceResponse>

Now, I can see that obviously, I don't have a prefix assigned to the namespace I told the elements to be a part of. My issue is that I cannot find a place to add the namespace prefixes in MVC4 using the media formatter. I have the following in my global.asax:

GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true;
GlobalConfiguration.Configuration.Formatters.XmlFormatter.RemoveSerializer(typeof(Models.ServiceResponse));
GlobalConfiguration.Configuration.Formatters.XmlFormatter.SetSerializer(typeof(Models.ServiceResponse), new Infrastructure.NamespaceXmlSerializer(typeof(Models.ServiceResponse)));

I made a custom serializer based on XmlSerializer in an attempt to intercept the writing request and tack the namespace list on there. The issue with this method is that right now I have breakpoints inside every overrideable method and none of them are tripped when serializing which leads me to believe that my serializer isn't being used.

Is there some built in way to accomplish what I want to do or am I stuck re-implementing the XmlMediaTypeFormatter to pass in namespaces when it serializes objects?


回答1:


As a followup answer: The easiest solution for me was to write my own XmlMediaTypeFormatter. As it turns out, its not nearly as intimidating as I thought.

public class NamespacedXmlMediaTypeFormatter : XmlMediaTypeFormatter 
{
    const string xmlType = "application/xml";
    const string xmlType2 = "text/xml";

    public XmlSerializerNamespaces Namespaces { get; private set; }

    Dictionary<Type, XmlSerializer> Serializers { get; set; }

    public NamespacedXmlMediaTypeFormatter()
        : base()
    {
        this.Namespaces = new XmlSerializerNamespaces();
        this.Serializers = new Dictionary<Type, XmlSerializer>();
    }

    public override Task WriteToStreamAsync(Type type, object value, System.IO.Stream writeStream, System.Net.Http.HttpContent content, System.Net.TransportContext transportContext)
    {
        lock (this.Serializers)
        {
            if (!Serializers.ContainsKey(type))
            {
                var serializer = new XmlSerializer(type);
                //we add a new serializer for this type
                this.Serializers.Add(type, serializer);
            }
        }

        return Task.Factory.StartNew(() =>
        {
            XmlSerializer serializer;
            lock (this.Serializers)
            {
                serializer = Serializers[type];
            }

            var xmlWriter = new XmlTextWriter(writeStream, Encoding.UTF8);
            xmlWriter.Namespaces = true;
            serializer.Serialize(xmlWriter, value, this.Namespaces);
        });
    }
}

Here is the formatter as a gist: https://gist.github.com/kcuzner/eef239003d4f99dfacea

The formatter works by exposing the XmlSerializerNamespaces that the XmlSerializer is going to use. This way I can add arbitrary namespaces as needed.

My top model looks as follows:

[XmlRoot("serviceResponse", Namespace="http://www.yale.edu/tp/cas")]
public class ServiceResponse
{
    [XmlElement("authenticationSuccess")]
    public CASAuthenticationSuccess Success { get; set; }

    [XmlElement("authenticationFailure")]
    public CASAuthenticationFailure Failure { get; set; }
}

In my Global.asax I added the following to place my formatter on the top of the list:

var xmlFormatter = new Infrastructure.NamespacedXmlMediaTypeFormatter();
xmlFormatter.Namespaces.Add("cas", "http://www.yale.edu/tp/cas");
GlobalConfiguration.Configuration.Formatters.Insert(0, xmlFormatter);

After adding the formatter and making sure my attributes were set up properly, my XML was properly namespaced.

In my case, I needed to add the cas namespace linking to http://www.yale.edu/tp/cas. For others using this, just change/replicate the Add call to your heart's content adding namespaces.



来源:https://stackoverflow.com/questions/17327677/xml-namespaces-in-asp-net-web-api

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!