【问题标题】:Why does the XmlRoot attribute gets ignored in WCF and how to overcome this为什么 XmlRoot 属性在 WCF 中被忽略以及如何克服这个问题
【发布时间】:2010-01-06 14:50:56
【问题描述】:

我们观察到,当我们公开一个使用各种 xml 序列化属性修饰的类的 WCF 服务时,尽管我们在接口上使用了 XmlSerializerFormat 属性,但任何操作参数上的任何 XmlRoot 属性都会被完全忽略。 参数的名称空间始终是服务的名称空间,而不是我们指定的名称空间。

这给我们带来了问题,因为它似乎不向后兼容 ASMX,还因为我们正在使用 BizTalk,并且需要更严格地控​​制交换的 XML 的形状。

那么有几个问题——

  1. 任何人都知道原因是什么 这个决定的背后是什么?
  2. 谁都知道 这是怎么回事?我在 WCF 的印象,与 XmlSerializerFormat 属性,使用 用于序列化的 XmlSerializer 类型,这将建议 XmlRoot 应该考虑到如何 来了不是这样吗? (是吗 只是因为,采取 考虑到 SOAP 信封, 参数不是root?)
  3. 大多数 重要的是 - 任何人都知道 有一种方法可以“强制解决问题”- 即获取参数为 我们选择的命名空间?

我看过 this 的帖子,但我认为这与我的问题无关 -

根据 Wagner Silveira 的要求 - 我用来测试的合同是 -

[ServiceContract(Namespace = "http://servicecontract"),
 XmlSerializerFormat(Style = OperationFormatStyle.Document)]
public interface ITestService
{
    [OperationContract]
    MyOtherType MyTestMethod(MyType obj);
}

// Composite class for DCS and XMLS
[Serializable, XmlType, XmlRoot(Namespace = "http://datacontract")] 
public class MyType
{
    [XmlAttribute]
    public string StringValue { get; set; }
}

// Composite class for DCS and XMLS
[Serializable, XmlType, XmlRoot(Namespace = "http://datacontract")]
public class MyOtherType
{
    [XmlAttribute]
    public string OtherStringValue { get; set; }
}

【问题讨论】:

    标签: wcf xml-serialization


    【解决方案1】:

    我假设您使用 SOAP 作为消息格式。在这种情况下,您要序列化的对象不是 XML 的根,肥皂信封才是。因此,忽略 XmlRoot 是有道理的。默认情况下,WCF 将为您创建一个消息协定并命名响应,并且它具有服务的命名空间。您可以通过create your own message contract 完全控制 SOAP。

    创建以下两个类:

    [MessageContract]
    public class MyTestMethodRequest
    {
        [MessageBodyMember( Namespace = "http://datacontract" )]
        public MyType MyType;
    }
    
    [MessageContract]
    public class MyTestMethodResponse
    {
        [MessageBodyMember( Namespace = "http://datacontract" )]
        public MyOtherType MyOtherType;
    }
    

    然后将您的服务操作的签名更改为以下内容。

    [OperationContract]
    public MyTestMethodResponse MyTestMethod( MyTestMethodRequest request )
    {
        return new MyTestMethodResponse {
            MyOtherType = new MyOtherType {
                OtherStringValue = "bar"
            }
        };
    }
    

    现在,如果您举例说明 SOAP 消息,您应该会看到以下内容:

    请求

    <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
      <s:Header>
        <Action xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none"
                s:mustUnderstand="1">http://servicecontract/TestService/MyTestMethod</Action>
      </s:Header>
      <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <MyTestMethodRequest xmlns="http://servicecontract">
          <MyType StringValue="foo" xmlns="http://datacontract" />
        </MyTestMethodRequest>
      </s:Body>
    </s:Envelope>
    

    回应

    <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
      <s:Header />
      <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <MyTestMethodResponse xmlns="http://servicecontract">
          <MyOtherType OtherStringValue="bar" xmlns="http://datacontract" />
        </MyTestMethodResponse>
      </s:Body>
    </s:Envelope>
    

    【讨论】:

    • 谢谢 John 我们想到了 MessageContract 选项,对于刚刚在 ASMX 中工作的东西来说似乎很麻烦,这也是我不确定非根节点参数的原因,但我想这是有道理的。我个人认为这对向后兼容性是一个很大的打击。
    【解决方案2】:

    我不知道为什么 WCF 会忽略 XmlRoot,所以我无法回答您的那部分问题。但我确实有几种方法可以解决这个问题。

    1. 首先从 WSDL 开始。
      如果您希望将一组特定的 XML 命名空间应用于发送和接收的消息,请使用 WSDL 和 XML Schema 明确指定它们。

      然后,通过the svcutil.exe tool 直接从该 WSDL 生成服务器端存根代码或客户端代理代码。

    2. use a custom ServiceHost
      在this link 中描述的另一个向您开放的选项是使用自定义 ServiceHost 来覆盖 WCF 忽略消息类型上的 XmlRoot 或 XmlType 属性的决定。


    如果您选择使用 WSDL-First 方法,则 WSDL 应如下所示:

    <?xml version="1.0" encoding="utf-8" ?>
    
    <definitions
        xmlns="http://schemas.xmlsoap.org/wsdl/"
        targetNamespace="urn:The-Service-namespace"
        xmlns:tns="urn:The-Service-namespace"
        xmlns:s="http://www.w3.org/2001/XMLSchema"
        xmlns:n0="urn:The-Request-namespace"
        xmlns:n1="urn:The-Response-namespace"
        xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
        elementFormDefault= "unqualified"
      >
    
        <types>
          <s:schema targetNamespace="urn:The-Request-namespace" >
            <s:complexType name="Type1">
              <s:sequence>
                <s:element name="x" minOccurs="1" maxOccurs="1" type="s:string"/>
              </s:sequence>
            </s:complexType>
            <s:element name="Type1" type="n0:Type1" />
          </s:schema>
    
    
          <s:schema targetNamespace="urn:The-Response-namespace" >
            <s:complexType name="Type2">
              <s:sequence>
                <s:element name="x" minOccurs="1" maxOccurs="1" nillable="false" type="s:string"/>
                <s:element name="y" minOccurs="1" maxOccurs="1" nillable="false" type="s:int"/>
                <s:element name="z" minOccurs="1" maxOccurs="1" nillable="false" type="s:boolean" />
              </s:sequence>
            </s:complexType>
            <s:element name="Type2" type="n1:Type2" />
          </s:schema>
    
        </types>
    
    
    
    <message name="RequestMessage">
       <part name="inPart1" element="n0:Type1" />
    </message>
    <message name="ResponseMessage">
       <part name="outPart1" element="n1:Type2" />
    </message>
    
    
    
    <portType name="PortTypeName">
      <operation name="Method1">
          <input message="tns:RequestMessage" />
          <output message="tns:ResponseMessage" />
       </operation>
    </portType>
    
    
    
    <binding name="InterfaceName" type="tns:PortTypeName">
        <soap:binding
           transport="http://schemas.xmlsoap.org/soap/http"
           style="rpc" />
    
        <operation name="Method1">
            <soap:operation soapAction="" style="document" />
            <input>  <soap:body use="literal" /> </input>
            <output> <soap:body use="literal" /> </output>
        </operation>
    </binding>
    
    </definitions>
    

    这个 WSDL 非常简单 - 它定义了一个操作,一个请求消息和一个响应消息。

    注意有三个 xml 命名空间:

    • urn:The-Service-命名空间
      用于包装请求和响应的元素 -
    • 中的第一个元素
    • urn:The-Request-namespace
      用于包装在该请求包装器中的元素,该元素被反序列化为 Type1 的实例。
    • urn:The-Response-namespace
      用于包装在该响应包装器中的元素,该响应包装器被反序列化为 Type2 的实例。

    如果您的 Web 服务界面更复杂,有更多操作,因此请求和响应消息类型也更多,您可以根据需要为所有这些附加类型添加更多命名空间。

    【讨论】:

    • 谢谢芝士。我熟悉自定义主机方法,事实上 - 这是我们在几个地方所做的,但这似乎是一个很大的要求,对于一种带有互操作性旗帜的技术。无论如何-我的问题的目的是了解行为。感谢您花时间回答。
    猜你喜欢
    • 2021-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-10
    相关资源
    最近更新 更多