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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/482856/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 12:19:59  来源:igfitidea点击:

Remove xml namespaces from WCF restful response

xmlwcfserializationnamespaces

提问by Duncan

I am using WCF to return a plain old XML (POX) document to the caller. I am using the XML Serializer formatter to turn the objects into XML.

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

In the returned document I have some extraneous xml namespace references (that weren't there in the ASMX version) for the XML Schema and instance. I have seen various arguments on the web that these shouldn't be removed which I don't buy into for returning a plain XML document.

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

What is the simplest way of removing these xmlns references from a returned XML document in WCF?

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

The signature looks like:

签名看起来像:

public ResponseInfo Process(string input) {
}

回答by Kevin Babcock

You can remove the XML namespace by setting the Namespace parameter of the DataContract attribute to an empty string, like so:

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

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

I hope this helps...

我希望这有帮助...

回答by user565923

I had the same problem. Adding BodyStyle:=WebMessageBodyStyle.Bareto WebInvoke worked for me. Response is no longer wrapped in a metadata.

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

回答by christophercotton

I assume you are trying instead of getting something like this at the beginning of your xml:

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

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

You want just:

你只想:

<ResponseInfo>

Sadly, I haven't seen an easy way yet to remove those fields. I was googling for solutions and most of the options for removing it require creating your own Message inspector, or your own encoder.

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

回答by Bernie

If you want to change Xml, one of the ways is to use an XslTransform. I had a similar case, where I needed to remove the xmlns attributes from an Xml Post request.

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

In WCF you can 'intercept' the Xml messages before the go out, or before they are processed on the way in, by implementing either the IClientMessageInspector or the IDispatchMessageInspector, depending on whether you need this at the client or the server side.

在 WCF 中,您可以通过实现 IClientMessageInspector 或 IDispatchMessageInspector 来“拦截”Xml 消息,这取决于您是在客户端还是在服务器端需要它。

For instance, to strip the namespace attributes from an outgoing Xml message to a web service, I implemented the IClientMessageInspector, using the following code:

例如,要将命名空间属性从传出 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

and used the following transform:

并使用以下转换:

    <?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>

Hope this is helpfull.

希望这有帮助。

回答by Lawrence Johnston

I found a good solution to this issue which lets you inject your own XmlSerializer into WCF that is used when serializing and deserializing requests. This XmlSerializer can be set up to omit XML namespaces entirely (including xmlns:i="w3.org/2001/XMLSchema-instance") or any other way you desire.

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

My solution utilizes WcfRestContrib. You could almost use the included POX formatterbut in our case we wanted to support attributes so we wrote our own simple formatter.

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

Instructions:

指示:

1) Reference WcfRestContrib from your project.

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

2) Create an IWebFormatterimplementation:

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

Which utilizes an XmlSerializer that fits your needs (here's ours which simply omits all namespaces):

它利用了一个满足您需要的 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) Apply the WebDispatchFormatter...attributes to your service using your custom implementation as the type (based on this documentation):

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

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

4) Apply the WebDispatchFormatterattribute to all of your service methods (based on this documentation).

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

5) That's it. Test your service and confirm it now behaves as expected.

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

回答by Steven King

Not sure if this will help, but we had a similar issue. Instead of decorating thousands of data elements with DataContract/DataMember attributes and using the (default) DataContractSerializer, we found that if our WCF service used the XmlSerializerFormat instead, we could easily deserialize our objects.

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

This is how we're deserializing the objects:

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

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

回答by Simon Gibbs

Just to give the other perspective, if the namespace is unique to your project, e.g:

只是从另一个角度来看,如果命名空间对于您的项目是唯一的,例如:

http://mycompany.com/myapi/

http://mycompany.com/myapi/

then it should be retained.

那么它应该被保留。

Such namespaces are best practice, they'll add less than 1 line of boilerplate to any XPath calls (which you can turn into a helper method) and require about 15 lines of helper code to generate a prefix/URI map, but that is the ONLY downside and you won't always encounter it.

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

In exchange you get unambiguous names for every element and that means you can compose third party namespaces with impunity e.g. in theory you could return XHTML directly without application level encoding.

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

Other namespaces turning up is unlikely to be an issue, as they are unlikely to be used on any of the tags that you defined in your project. In fact, if they are, then there is a bug somewhere. The likely explanaintion for there existence is that the framework added them just in case it needed to add an element somewhere below the spot where they are declared, which may not be a location you have to care about.

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

Another answer mentioned http://www.w3.org/2001/XMLSchema-instancewhich is a bit special, perhaps you could say what namespaces were added.

另一个答案提到http://www.w3.org/2001/XMLSchema-instance有点特别,也许您可​​以说添加了哪些名称空间。

回答by Sailing Judo

In my RESTful WCF service which I wrote prior to the WCF RESTful starter kit I did the following which gives me nice, clean results.

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

First, make sure the webHttp endpoint behavior is set:

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

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

Your endpoint should look something like this (minus the contract for simplicity):

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

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

My service contract has these operation contracts in it:

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

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

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

The service implementation itself has nothing special about it. You can try changing up the WebMessageFormat but the only other item in the enumeration is "json".

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

回答by Qais Khateeb

I have the same problem when I work with ASMX clients and For me this solve the problem :

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

Add to your Service Interface :

添加到您的服务接口:

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

Add to Operations :

添加到操作:

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