为什么在WCF中忽略XmlRoot属性以及如何克服这个问题

时间:2010-01-06 14:50:57

标签: wcf xml-serialization

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

这引起了我们的问题,因为它似乎没有与ASMX向后兼容,也因为我们正在使用BizTalk,并且需要更严格地控​​制XML交换的形状。

然后有几个问题 -

  1. 任何人都知道什么是理由 这个决定背后?
  2. 有人知道 怎么回事?我在 WCF带来的印象 XmlSerializerFormat属性,使用 XmlSerialiser序列化 类型,这将建议XmlRoot 应该考虑如何 来不是这样的吗? (是吗 只是因为这个事实 考虑到SOAP信封, 参数不是root?)
  3. 最 重要的是 - 任何人都知道 有办法'迫使问题' - 即获取参数 我们选择的命名空间?
  4. 我看过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; }
    }
    

2 个答案:

答案 0 :(得分:3)

我假设你使用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>

答案 1 :(得分:1)

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

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

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

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


  3. 如果您选择使用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命名空间:

    • 瓮:所述服务 - 名称空间
      用于包装请求和响应的元素 - &lt; SOAP:body&gt;
    • 中的第一个元素
    • 瓮:所述请求的命名空间
      用于包含在请求包装器内的元素,该包装器被反序列化为Type1的实例。
    • 瓮:本 - 响应 - 命名空间
      用于包含在响应包装器内的元素,该元素被反序列化为Type2的实例。

    如果您的Web服务接口更复杂,有更多操作,因此有更多请求和响应消息类型,您可以为所有这些其他类型添加更多名称空间(如果您愿意)。