Marius Bancila
Marius Bancila

Reputation: 16338

Incorrect deserialization of SOAP message in .NET app

I am working on converting an existing application from .NET Framework to .NET 6. Deserialization of SOAP message no longer works as expected.

I have the following response message:

<?xml version="1.0" encoding="utf-16"?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
   <s:Header xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" />
   <S:Body>
      <ns2:findAgreementResponse
         xmlns:ns2="example.webservices.licensing.secure"
         xmlns:ns3="example.webservices.exceptions">
         <return>
            <description>Standard</description>
            <idLicenseAgreement>1234</idLicenseAgreement>
         </return>
      </ns2:findAgreementResponse>
   </S:Body>
</S:Envelope>

With the .NET Framework, the code generated when I added a service reference included this:

[System.Xml.Serialization.XmlIncludeAttribute(typeof(agreement))]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.7.3221.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://example.org/licensing/")]
public partial class messageBase {     
     private System.Xml.XmlElement[] anyField;     
     private string messageIdField;
     
     [System.Xml.Serialization.XmlAnyElementAttribute()]
     public System.Xml.XmlElement[] Any {
         get { return this.anyField; }
         set { this.anyField = value; }
     }
     
     [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
     public string messageId {
         get { return this.messageIdField; }
         set { this.messageIdField = value; }
     }
}
 
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.7.3221.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://example.org/licensing/")]
public partial class agreement : messageBase {
     
     private string descriptionField;     
     private long idLicenseAgreementField;     
     private bool idLicenseAgreementFieldSpecified;
     
     [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
     public string description {
         get { return this.descriptionField; }
         set { this.descriptionField = value; }
     }
     
     [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
     public long idLicenseAgreement {
         get { return this.idLicenseAgreementField; }
         set { this.idLicenseAgreementField = value; }
     }
     
     [System.Xml.Serialization.XmlIgnoreAttribute()]
     public bool idLicenseAgreementSpecified {
         get { return this.idLicenseAgreementFieldSpecified; }
         set { this.idLicenseAgreementFieldSpecified = value; }
     }
}

[System.Web.Services.Protocols.SoapDocumentMethodAttribute("", RequestNamespace="example.webservices.licensing.secure", ResponseNamespace="example.webservices.licensing.secure", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
[return: System.Xml.Serialization.XmlElementAttribute("return", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public agreement findAgreement([System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] string arg0) {
   object[] results = this.Invoke("findAgreement", new object[] {
               arg0});
   return ((agreement)(results[0]));
}

This worked well, the response was properly deserialized (when calling the findAgreement method). When something was not expected (there are many methods and responses), it was added to the Any array from messageBase.

After migrating to .NET 6, adding the service resulted in the following code:

[System.SerializableAttribute()]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(agreement))]
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://example.org/licensing/")]
public partial class messageBase
{
     private System.Xml.XmlElement[] anyField;
     private string messageIdField;
     
     [System.Xml.Serialization.XmlAnyElementAttribute(Order=0)]
     public System.Xml.XmlElement[] Any
     {
         get { return this.anyField; }
         set { this.anyField = value; }
     }
     
     [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=1)]
     public string messageId
     {
         get { return this.messageIdField; }
         set { this.messageIdField = value; }
     }
}

[System.SerializableAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://example.org/licensing/")]
public partial class agreement : messageBase
{
     private string descriptionField;
     private long idLicenseAgreementField;
     private bool idLicenseAgreementFieldSpecified;
     
     [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=0)]
     public string description
     {
         get { return this.descriptionField; }
         set { this.descriptionField = value; }
     }
     
     [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=1)]
     public long idLicenseAgreement
     {
         get { return this.idLicenseAgreementField; }
         set { this.idLicenseAgreementField = value; }
     }
     
     [System.Xml.Serialization.XmlIgnoreAttribute()]
     public bool idLicenseAgreementSpecified
     {
         get { return this.idLicenseAgreementFieldSpecified; }
         set { this.idLicenseAgreementFieldSpecified = value; }
     }
}


[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")]
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
[System.ServiceModel.MessageContractAttribute(WrapperName="findAgreementResponse", WrapperNamespace="example.webservices.licensing.secure", IsWrapped=true)]
public partial class findAgreementResponse
{
     
     [System.ServiceModel.MessageBodyMemberAttribute(Namespace="example.webservices.licensing.secure", Order=0)]
     [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
     public agreement @return;
     
     public findAgreementResponse()
     {
     }
     
     public findAgreementResponse(agreement @return)
     {
         this.@return = @return;
     }
}

[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
findAgreementResponse findAgreement(findAgreementRequest request)
{
   return base.Channel.findAgreement(request);
}

What happens now is that the description and idLicenseAgreement properties are not filled in. Instead, the Any array from the base class contains both these XML elements:

<description>Standard</description>
<idLicenseAgreement>1234</idLicenseAgreement>

If I remove the messageBase base class, parsing occurs correctly, and the agreement object is properly filled in with the data from the SOAP response.

I expect this to be a namespace issue but I was unable to figure it out.

Upvotes: 1

Views: 486

Answers (1)

Marius Bancila
Marius Bancila

Reputation: 16338

I figured out the problem was the explicit ordering of elements. In the code generated by Svcutil in .NET 6, all the XML elements are attributed like this:

[System.Xml.Serialization.XmlAnyElementAttribute(Order=0)]

Once I removed the Order property from all of them, deserialization worked as expected.

Upvotes: 1

Related Questions