Reputation: 16338
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
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