从 WCF RESTful 响应中删除 xml 命名空间

Posted

技术标签:

【中文标题】从 WCF RESTful 响应中删除 xml 命名空间【英文标题】:Remove xml namespaces from WCF restful response 【发布时间】:2010-10-03 17:29:33 【问题描述】:

我正在使用 WCF 向调用者返回一个普通的旧 XML (POX) 文档。我正在使用 XML Serializer 格式化程序将对象转换为 XML。

在返回的文档中,我有一些针对 XML Schema 和实例的无关 xml 命名空间引用(在 ASMX 版本中不存在)。我在网上看到了各种不应该删除这些的论点,我不赞成返回纯 XML 文档。

从 WCF 中返回的 XML 文档中删除这些 xmlns 引用的最简单方法是什么?

签名看起来像:

public ResponseInfo Process(string input) 

【问题讨论】:

【参考方案1】:

您可以通过将 DataContract 属性的 Namespace 参数设置为空字符串来移除 XML 命名空间,如下所示:

[DataContract(Namespace = "")]
public class ResponseInfo

    // ...

我希望这会有所帮助...

【讨论】:

这可行,但您可能需要将 [DataMember] 属性放在字段上以确保 xml 不为空 从我的测试来看,这个方法并没有摆脱 xmlns:i="w3.org/2001/XMLSchema-instance" 声明【参考方案2】:

我遇到了同样的问题。 将 BodyStyle:=WebMessageBodyStyle.Bare 添加到 WebInvoke 对我有用。 响应不再包含在元数据中。

【讨论】:

我有 BodyStyl=WebMessageBodyStyle.Wrapper,将其更改为 BodyStyle=WebMessageBodyStyle.Bare 解决了我的问题。 伟大的斯科特!太棒了。感谢您提供有用的标签! 这对命名空间的存在没有影响【参考方案3】:

我假设你正在尝试而不是在你的 xml 开头得到这样的东西:

<ResponseInfo 
   xmlns="http://schemas.datacontract.org/2004/07/ResponseInfo"
   xmlns:i="http://www.w3.org/2001/XMLSchema-instance" >

你想要的只是:

<ResponseInfo>

遗憾的是,我还没有看到删除这些字段的简单方法。我在谷歌上搜索解决方案,大多数删除它的选项都需要创建您自己的消息检查器或您自己的编码器。

【讨论】:

【参考方案4】:

如果要更改 Xml,其中一种方法是使用 XslTransform。我有一个类似的情况,我需要从 Xml Post 请求中删除 xmlns 属性。

在 WCF 中,您可以通过实现 IClientMessageInspector 或 IDispatchMessageInspector,在外出之前或在它们被处理之前“拦截” Xml 消息,具体取决于您在客户端还是服务器端需要它.

例如,为了将传出 Xml 消息中的命名空间属性剥离到 Web 服务,我使用以下代码实现了 IClientMessageInspector:

#region IClientMessageInspector Members
    public void AfterReceiveReply(ref Message reply, object correlationState)
       
        //Console.WriteLine(reply.ToString());
    

    private XslCompiledTransform xt = null;

    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    
        Console.WriteLine(request.ToString());
        if (!request.IsEmpty)
        
            XmlReader bodyReader =
                request.GetReaderAtBodyContents().ReadSubtree();

            MemoryStream ms = new MemoryStream();
            XmlWriter xw = XmlWriter.Create(ms);

            if (xt == null)
            
                xt = new XslCompiledTransform(true);
                xt.Load("StripXmlnsi.xslt");
            
            xt.Transform(bodyReader, xw);

            ms.Flush();
            ms.Seek(0, SeekOrigin.Begin);

            bodyReader = XmlReader.Create(ms);

            Message changedMessage = Message.CreateMessage(request.Version, null, bodyReader);
            changedMessage.Headers.CopyHeadersFrom(request.Headers);
            changedMessage.Properties.CopyProperties(request.Properties);
            request = changedMessage;
        
        return null;
    
    #endregion

并使用了以下变换:

    <?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="*">
    <!-- remove element prefix (if any) -->
    <xsl:element name="local-name()">
      <!-- process attributes -->
      <xsl:for-each select="@*">
        <!-- remove attribute prefix (if any) -->
        <xsl:attribute name="local-name()">
          <xsl:value-of select="." />
        </xsl:attribute>
      </xsl:for-each>
      <xsl:apply-templates />
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

希望这会有所帮助。

【讨论】:

这是一个糟糕的解决方案。你真的认为为了移除命名空间而编写所有这些代码就可以了吗? 是的,小尺寸不等于美丽。如果您有更简单的解决方案,请随时发布,但仅仅因为需要超过 3 行代码而忽略某些东西并不是一个体面的工程师会做的事情。【参考方案5】:

我找到了一个很好的解决这个问题的方法,它可以让你将自己的 XmlSerializer 注入到 WCF 中,在序列化和反序列化请求时使用它。此 XmlSerializer 可以设置为完全省略 XML 命名空间(包括 xmlns:i="w3.org/2001/XMLSchema-instance")或您希望的任何其他方式。

我的解决方案使用WcfRestContrib。您几乎可以使用包含的 POX formatter,但在我们的例子中,我们希望支持属性,因此我们编写了自己的简单格式化程序。

说明:

1) 从您的项目中引用 WcfRestContrib。

2) 创建一个IWebFormatter 实现:

public class NamespacelessXmlFormatter : IWebFormatter 
    public object Deserialize(WebFormatterDeserializationContext context, Type type) 
        if (context.ContentFormat != WebFormatterDeserializationContext.DeserializationFormat.Xml) 
            throw new InvalidDataException("Data must be in xml format.");
        

        return NamespacelessXmlSerializer.Deserialize(context.XmlReader, type);
    

    public WebFormatterSerializationContext Serialize(object data, Type type) 
        using (var stream = NamespacelessXmlSerializer.Serialize(data, type)) 
            using (var binaryReader = new BinaryReader(stream)) 
                byte[] bytes = binaryReader.ReadBytes((int)stream.Length);
                return WebFormatterSerializationContext.CreateBinary(bytes);
            
        
    

它利用了满足您需求的 XmlSerializer(这是我们的,它只是省略了所有命名空间):

public static class NamespacelessXmlSerializer 

    private static readonly XmlSerializerNamespaces _customNamespace = new XmlSerializerNamespaces();

    private static readonly XmlWriterSettings _xmlSettings = new XmlWriterSettings 
        OmitXmlDeclaration = true
    ;

    static NamespacelessXmlSerializer() 
        // to make sure .NET serializer doesn't add namespaces
        _customNamespace.Add(String.Empty, String.Empty);
    

    /// <summary>
    /// Deserializes object from its XML representation.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="stream"></param>
    /// <returns></returns>
    public static T Deserialize<T>(Stream stream) 
        return (T)Deserialize(stream, typeof(T));
    

    /// <summary>
    /// Deserializes object from its XML representation.
    /// </summary>
    public static object Deserialize(Stream stream, Type type) 
        var ds = new XmlSerializer(type);
        var d = ds.Deserialize(stream);
        return d;
    

    public static object Deserialize(XmlDictionaryReader xmlReader, Type type) 
        var ds = new XmlSerializer(type);
        var d = ds.Deserialize(xmlReader);
        return d;
    

    /// <summary>
    /// Serializes object to XML representation.
    /// </summary>
    /// <exception cref="InvalidOperationException">
    /// Is thrown when there was an error generating XML document. This can happen 
    /// for example if the object has string with invalid XML characters:
    /// http://www.w3.org/TR/2004/REC-xml-20040204/#charsets.
    /// See this article for other potential issues:
    /// http://msdn.microsoft.com/en-us/library/aa302290.aspx
    /// </exception>
    public static Stream Serialize<T>(T objectToSerialize) 
        return Serialize(objectToSerialize, typeof(T));
    

    /// <summary>
    /// Serializes object to XML representation.
    /// </summary>
    /// <exception cref="InvalidOperationException">
    /// Is thrown when there was an error generating XML document. This can happen 
    /// for example if the object has string with invalid XML characters:
    /// http://www.w3.org/TR/2004/REC-xml-20040204/#charsets.
    /// See this article for other potential issues:
    /// http://msdn.microsoft.com/en-us/library/aa302290.aspx
    /// </exception>
    public static Stream Serialize(object objectToSerialize, Type type) 
        var stream = new MemoryStream();

        XmlWriter writer = XmlWriter.Create(stream, _xmlSettings);
        var x = new XmlSerializer(type);
        x.Serialize(writer, objectToSerialize, _customNamespace);

        stream.Position = 0;

        return stream;
    

3) 使用您的自定义实现作为类型将WebDispatchFormatter... 属性应用于您的服务(基于此documentation):

[WebDispatchFormatterConfiguration("application/xml")]
[WebDispatchFormatterMimeType(typeof(NamespacelessXmlFormatter), "application/xml")]

4) 将WebDispatchFormatter 属性应用于您的所有服务方法(基于此documentation)。

5) 就是这样。测试您的服务并确认它现在按预期运行。

【讨论】:

这看起来像是我的答案。【参考方案6】:

如果命名空间对您的项目来说是唯一的,例如:

http://mycompany.com/myapi/

那么它应该被保留。

这样的命名空间是最佳实践,它们将向任何 XPath 调用添加不到 1 行样板代码(您可以将其转换为辅助方法),并且需要大约 15 行辅助代码来生成前缀/URI 映射,但是这是唯一的缺点,你不会总是遇到它。

作为交换,您可以获得每个元素的明确名称,这意味着您可以不受惩罚地组成第三方命名空间,例如理论上,您可以直接返回 Xhtml 而无需应用程序级编码。

出现的其他命名空间不太可能成为问题,因为它们不太可能用于您在项目中定义的任何标签。事实上,如果它们是,那么某处存在错误。存在的可能解释是框架添加它们只是为了以防它需要在声明它们的位置下方的某个位置添加一个元素,这可能不是你必须关心的位置。

另一个回答提到http://www.w3.org/2001/XMLSchema-instance,有点特殊,或许你可以说一下添加了哪些命名空间。

【讨论】:

【参考方案7】:

不确定这是否会有所帮助,但我们遇到了类似的问题。我们发现,如果我们的 WCF 服务使用 XmlSerializerFormat 代替,我们可以轻松地反序列化我们的对象,而不是使用 DataContract/DataMember 属性装饰数千个数据元素并使用(默认)DataContractSerializer。

[System.ServiceModel.ServiceContract]
public interface IRestService

    [System.ServiceModel.OperationContract]
    // Added this attribute to use XmlSerializer instead of DataContractSerializer
    [System.ServiceModel.XmlSerializerFormat(
        Style=System.ServiceModel.OperationFormatStyle.Document)]
    [System.ServiceModel.Web.WebGet(
        ResponseFormat = System.ServiceModel.Web.WebMessageFormat.Xml,
        UriTemplate = "xml/objects/myObjectIdentifier")]
    MyObject GetMyObject(int myObjectIdentifier);

这就是我们反序列化对象的方式:

public static T DeserializeTypedObjectFromXmlString<T>(string input)

    T result;

    try
    
        System.Xml.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer(typeof(T));
        using (System.IO.TextReader textReader = new System.IO.StringReader(input))
        
            result = (T)xs.Deserialize(textReader);
        
    
    catch
    
        throw;
    

    return result;

【讨论】:

【参考方案8】:

在我在 WCF RESTful 入门工具包之前编写的 RESTful WCF 服务中,我执行了以下操作,这给了我很好、干净的结果。

首先,确保设置了 webHttp 端点行为:

  <endpointBehaviors>
    <behavior name="Web">
      <webHttp/>
    </behavior>
  </endpointBehaviors>

您的端点应如下所示(为简单起见,减去合同):

<endpoint address="" binding="webHttpBinding" behaviorConfiguration="Web" />

我的服务合同中有这些操作合同:

    [WebGet(UriTemplate="tasks", ResponseFormat=WebMessageFormat.Xml )]
    [OperationContract]
    Task[] GetTasks();

    [WebGet(UriTemplate="tasks/id", ResponseFormat=WebMessageFormat.Xml)]
    [OperationContract]
    Task GetTask(string id);

服务实现本身并没有什么特别之处。您可以尝试更改 WebMessageFormat 但枚举中唯一的其他项目是“json”。

【讨论】:

如果反对者能发表评论,那就太好了。我的回答是 100% 正确和可行的。它可能不完全符合提问者的需求,或者也许有更好的方法,但它确实有效。 我不是反对者,但尽管您的回答可能是“正确的”,但它并没有回答这个特定问题。【参考方案9】:

我在使用 ASMX 客户端时遇到了同样的问题,对我来说这解决了问题:

添加到您的服务接口:

[XmlSerializerFormat(Use = OperationFormatUse.Literal, Style = OperationFormatStyle.Document)]

添加到操作:

[OperationContract(Action = "http://www.YourNameSpace.com/ActionName",ReplyAction = "http://www.YourNameSpace.com/ActionName")]

【讨论】:

以上是关于从 WCF RESTful 响应中删除 xml 命名空间的主要内容,如果未能解决你的问题,请参考以下文章

从wcf服务响应的结果中生成xml

C# Restful WCF 服务。无法在帖子正文中反序列化 XML

如何从 WCF 服务返回 xml 响应

如何从 SOAP 响应 XML 创建 WCF 消息对象

RESTful WCF 使用方法名称包装 json 响应

如何删除 RESTful WCF 服务中的“.svc”扩展名?