Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the XmlRoot attribute gets ignored in WCF and how to overcome this

We've observed that when we expose a WCF service which uses classes decorated with various xml serialisation attributes, despite the fact that we use the XmlSerializerFormat attribute on the interface any XmlRoot attribute on any of the operation's parameters gets completely ignored. The namespace of the parameters is always that of the service and not what we specify.

This is causing us problems as it does not seem to be backwards compatible with ASMX and also because we're using BizTalk, and need to have tighter control over the shape of the XML's exchanged.

A few questions then -

  1. Anybody knows what is the rationale behind this decision?
  2. Anybody knows how this is happening? I was under the impressions that WCF, with the XmlSerializerFormat attribute, uses the XmlSerialiser to serialise the types, which would suggest XmlRoot should be taken into account, how come this is not the case? (is it only due to the fact that, taking the SOAP envelope into account, the parameter is not root?)
  3. Most importantly - anybody knows if there's a way to 'force the issue' - i.e. get the parameters to be of the namespace of our choosing?

I've seen this post, but I don't believe it is relevant to my question -

As per Wagner Silveira's request - the contracts I used to test this are -

[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; }
}
like image 587
Yossi Dahan Avatar asked Jan 06 '10 14:01

Yossi Dahan


1 Answers

I assume you're using SOAP as the message format. In this case, the object you're serializing is not the root of the XML, the soap envelope is. So it makes sense that the XmlRoot would be ignored. By default WCF will create a message contract for you and name the response and it has the namespace of the service. What you can do is create your own message contract to have full control over SOAP.

Create the following two classes:

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

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

Then change the signature of your service operation to the following.

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

Now if you example the SOAP messages you should see the following:

Request

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

Response

<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>
like image 146
Josh Avatar answered Nov 22 '22 08:11

Josh