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

时间:2009-01-27 10:11:56

标签: xml wcf serialization namespaces

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

在返回的文档中,我为XML Schema和实例提供了一些无关的xml命名空间引用(在ASMX版本中没有)。我在网上看到了各种不应删除的论点,我不会为了返回普通的XML文档而购买这些论点。

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

签名如下:

public ResponseInfo Process(string input) {
}

9 个答案:

答案 0 :(得分:19)

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

[DataContract(Namespace = "")]
public class ResponseInfo
{
    // ...
}

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

答案 1 :(得分:7)

答案 2 :(得分:5)

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

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

你只想:

<ResponseInfo>

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

答案 3 :(得分: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>

希望这有用。

答案 4 :(得分:3)

我找到了一个很好的解决方案,可以让你将自己的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)使用自定义实现作为类型(基于此documentation)将WebDispatchFormatter...属性应用于您的服务:

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

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

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

答案 5 :(得分:2)

只是为了给出另一个视角,如果命名空间对于您的项目是唯一的,例如:

http://mycompany.com/myapi/

然后应该保留。

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

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

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

另一个提到http://www.w3.org/2001/XMLSchema-instance的答案有点特别,或许可以说明添加了什么名称空间。

答案 6 :(得分:2)

不确定这是否会有所帮助,但我们遇到了类似的问题。我们发现,如果我们的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;
}

答案 7 :(得分:1)

在我在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”。

答案 8 :(得分:1)

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

添加到您的服务界面:

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

添加到操作:

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