WCF: values disappeared in response: derived classes and serialization/deserialization order error

WCF: values disappeared in response: derived classes and serialization/deserialization order error
 
This is a second article about errors with getting data from  the web-services.
"WCF: Deserialization error, the response elements disappeared" ( http://geekswithblogs.net/LeonidGaneline/archive/2008/04/01/wcf-deserialization-error-the-response-elements-disappeared.aspx )
First time the whole elements disappeared from the response messages without any errors. Now only values of the elements disappeared.
 
We got a very strange error.
 
Our client code to one of the third-part web-service could not get the values from the response message.
 
Investigation given us such picture:
The response class has a base class. And the disappeared values are exactly from properties of this class. See the client proxy code that was generated by Visual Studio (Pict.1). Nothing wrong with it.
If we look at the wsdl that was the source for this proxy code (Pict.2) there is nothing wrong too.
The most strange thing is we have the right values in the response message (Pict.3). The errorCode and errorMessage elements have values but then we lost them. We can get them in the code (Pict.4). The output is on Pict.5.
How? Why?
The base class properties are the errorCode and errorMessage, the derived class property is the crn.
The question is in what order those two kind of properties serialize to the elements of the Xml message and then deserialize back to the class properties?
If service and client make serialization and deserialization in the "stack-like" order then everything is OK.
We see that service serialize the derived class properties first and the base class properties last (See Pict.3.1). What about our client? The client is waiting the message with different order of elements (see Pict.3.2)
And this is the source of the problem!
 

In the "Data Member Order " article in the MSDN Library ( http://msdn.microsoft.com/en-us/library/ms729813.aspx ) we see:
 
"...The basic rules for data ordering include:
If a data contract type is a part of an inheritance hierarchy, data members of its base types are always first in the order..."
 
And there is no way to change this behaviour.
 
That's exactly how my client proxy is working. It is waiting the base class properties on the first place and have not get them, and because these properties have minOccurs="0" and nillable="true" attributes the proxy just decided that Xml message does not have properties at all and silently goes on to the first derived class property.
Unfortunately the third-part service uses the opposite order (it uses a java Axis2 service), data members of its base types are always last in the order in the serialized Xml messages.
BTW Here (http://www.onjava.com/pub/a/onjava/excerpt/JavaRMI_10/index.html?page=4) is mentioned that Java uses the same algorithm as WCF. Why does this consuming service use the opposite order? I have no idea.
Jeff W. Barnes in (http://jeffbarnes.net/portal/blogs/jeff_barnes/archive/2007/05/08/wcf-serialization-order-in-data-contracts.aspx) mentioned thet there is the solution but does not give it.
 
Is it possible to change the proxy code on my side to follow the service order? I don't know such options. Seems the WCF-classes where the whole deserialization occurs cannot be tuned up to change this order. Look one more time to the wsdl code (Pict.2). There is nothing about this order, ther is only the complexType the AddNewCreditCardResponse with the extension base="ax21:ResponseData".
Seems the only way to fix the problem is to change the auto generated code for the response message classes and force the right order. (Pict.6) OK from the point of view of WCF it is a wrong code, but what else we could do in this situation?
 
After these changes we've got the right result (see Pict.7).
 
 
 
=======================================================================
[Pict.1: autogenerated proxy code]
    [System.SerializableAttribute()]
    [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://data.transaction.com/xsd")]
    public partial class AddNewCreditCardResponse : ResponseData {
       
        private string crnField;
        [System.Xml.Serialization.XmlElementAttribute(IsNullable=true, Order=0)]
        public string crn {
            get {   return this.crnField; }
            set { this.crnField = value; }
        }
    }
 ...
    [System.Xml.Serialization.XmlIncludeAttribute(typeof(AddNewCreditCardResponse))]
    [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://data.transaction.com/xsd")]
    public partial class ResponseData {
       
       private string errorCodeField;
       [System.Xml.Serialization.XmlElementAttribute(IsNullable=true, Order=0)]
        public string errorCode{
            get { return this.errorCodeField; }
            set {  this.errorCodeField= value; }
       }
 
        private string errorMessageField;
       [System.Xml.Serialization.XmlElementAttribute(IsNullable=true, Order=1)]
        public string errorMessage{
            get { return this.errorMessageField; }
            set {  this.errorMessageField= value; }
       }
    }
 

[Pict.2: wsdl code]
    <xs:complexType name="AddNewCreditCardResponse">
        <xs:complexContent mixed="false">
          <xs:extension base="ax21:ResponseData">
            <xs:sequence>
              <xs:element minOccurs="0" name="crn" nillable="true" type="xs:string" />
            </xs:sequence>
          </xs:extension>
        </xs:complexContent>
      </xs:complexType>
 ...
      <xs:complexType name="ResponseData">
        <xs:sequence>
          <xs:element minOccurs="0" name="errorCode" nillable="true" type="xs:string" />
          <xs:element minOccurs="0" name="errorMessage" nillable="true" type="xs:string" />
        </xs:sequence>
      </xs:complexType>...
 

[Pict.3.1: original response message]
 ...
 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Body>
       <ns:addNewCreditCardResponse xmlns:ns="http://service.transaction.com">
   <ns:return type="com.transaction.data.AddNewCreditCardResponseData" xmlns:ax21="http://data.transaction.com/xsd">
      <ax21:crn/>
      <ax21:errorCode>TXN056</ax21:errorCode>
      <ax21:errorMessage>Process auth with create profile error</ax21:errorMessage>
   </ns:return>
       </ns:addNewCreditCardResponse>
    </soapenv:Body>
 </soapenv:Envelope>
 
[Pict.3.2: response sample with "WCF" order]
 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Body>
       <ns:addNewCreditCardResponse xmlns:ns="http://service.transaction.com">
   <ns:return type="com.transaction.data.AddNewCreditCardResponse" xmlns:ax21="http://data.transaction.com/xsd">
      <ax21:errorCode>TXN0544</ax21:errorCode>
      <ax21:errorMessage>Process authentification with profile error</ax21:errorMessage>
      <ax21:crn/>
  </ns:return>
       </ns:addNewCreditCardResponse>
    </soapenv:Body>
 </soapenv:Envelope>
 
[Pict.4: test code]
 ...
 Transaction_Ref.TransactionSvcClient client = new TestTxsServiceConsoleApplication.Transaction_Ref.TransactionSvcClient ("TransactionSvcSOAP11port_http");
 
 Transaction_Ref.addNewCreditCardResponse addNewCreditCardResponse = client.addNewCreditCard(...);
 Console.WriteLine("crn: [{0}]", (addNewCreditCardResponse.crn == null ? "null" : addNewCreditCardResponse.crn));
 Console.WriteLine("errorCode: [{0}]", (addNewCreditCardResponse.errorCode == null ? "null" : addNewCreditCardResponse.errorCode));
 Console.WriteLine("errorMessage: [{0}]", (addNewCreditCardResponse.errorMessage == null ? "null" : addNewCreditCardResponse.errorMessage));
 ...
 

[Pict.5: output with original proxy code]
 crn: []
 errorCode: [null]
 errorMessage: [null]
 

[Pict.6: changed proxy code]
    [System.SerializableAttribute()]
    [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://data.transaction.com/xsd")]
    public partial class AddNewCreditCardResponse {
       
        private string crnField;
        [System.Xml.Serialization.XmlElementAttribute(IsNullable=true, Order=0)]
        public string crn {
            get { return this.crnField; }
            set {  this.crnField = value; }
       }
 
        private string errorCodeField;
       [System.Xml.Serialization.XmlElementAttribute(IsNullable=true, Order=1)]
        public string errorCode{
            get { return this.errorCodeField; }
            set {  this.errorCodeField= value; }
       }
 
        private string errorMessageField;
       [System.Xml.Serialization.XmlElementAttribute(IsNullable=true, Order=2)]
        public string errorMessage{
            get { return this.errorMessageField; }
            set {  this.errorMessageField= value; }
       }
 ...
 

[Pict.7: output with changed proxy code]
 crn: []
 errorCode: [TXN0544]
 errorMessage: [Process authentification with profile error]
=========================================
Please, give me a feedback!
Print | posted on Thursday, May 1, 2008 3:11 PM

Feedback

# re: WCF: values disappeared in response: derived classes and serialization/deserialization order error

left by Leng Keng at 11/21/2008 10:51 AM Gravatar
Thanks for the post. It helped me figured out the issue I was facing. I have a WCF client calling a Axis2 service.

After some testing, I found that the problem can be fixed by simply comment out the useless class property attribute in the generated proxy classes. For example:

//[System.Xml.Serialization.XmlElementAttribute(Order = 0)]
public string MyName
{ ... }

I didn't find any svcutil.exe flag that can suppress the generation of attribute order. This is particularly a problem when the response XML schema (xsd) contains fields that are optional, such as:
<xsd:element ref="MyName" minOccurs="0"/>
In which case "MyName" may or may not be sent back, causing the remaining elements to be null.

Since the generated code is not conforming to the schema specification, I would classify this as a WCF bug.

# re: WCF: values disappeared in response: derived classes and serialization/deserialization order error

left by Hoang Pham at 12/10/2009 3:32 AM Gravatar
Great solution, thanks to leng Keng. This helps me to solve the problem by deleting "Order = xxx" from the code.
I have another problem if in the reponse of the web service, if my subclass is found in other namespace, my wcf client can not serialize it. See below code for more information:
[System.CodeDom.Compiler.GeneratedCodeAttribute("svcutil", "3.0.4506.648")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
//[System.Xml.Serialization.XmlTypeAttribute(Namespace = "urn:21x.test.com:xsd:ServiceCode:G1")]
public partial class serviceCode
{
...
}

Post A Comment
Title:
Name:
Email:
Comment:
Verification: