5

我目前正在从事一个项目,该项目需要我从其端点输出 XML 以及 JSON。我有以下模型:

[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; }
}

当success不为null时输出如下:

<serviceResponse>
<authenticationSuccess />
</serviceResponse>

现在,我可以清楚地看到,我没有为命名空间分配前缀,我告诉元素要成为其中的一部分。我的问题是我找不到使用媒体格式化程序在 MVC4 中添加命名空间前缀的地方。我的 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)));

我基于 XmlSerializer 制作了一个自定义序列化程序,试图拦截写入请求并在那里添加命名空间列表。这种方法的问题是,现在我在每个可覆盖的方法中都有断点,并且在序列化时没有一个断点被触发,这让我相信我的序列化程序没有被使用。

是否有一些内置方法可以完成我想做的事情,或者我是否坚持重新实现 XmlMediaTypeFormatter 以在序列化对象时传入命名空间?

4

1 回答 1

4

作为后续回答:对我来说最简单的解决方案是编写自己的XmlMediaTypeFormatter. 事实证明,它并没有我想象的那么可怕。

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);
        });
    }
}

这是作为要点的格式化程序:https ://gist.github.com/kcuzner/eef239003d4f99dfacea

格式化程序通过公开XmlSerializerNamespacesXmlSerializer要使用的 来工作。这样我可以根据需要添加任意命名空间。

我的顶级模型如下所示:

[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; }
}

在我Global.asax的中,我添加了以下内容以将我的格式化程序放在列表的顶部:

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

添加格式化程序并确保我的属性设置正确后,我的 XML 被正确命名空间。

就我而言,我需要添加cas链接到http://www.yale.edu/tp/cas. 对于使用此功能的其他人,只需更改/复制Add对您心脏内容的调用添加命名空间。

于 2014-10-20T22:53:00.867 回答