Savage
Savage

Reputation: 2349

SOAP WCF: Prevent deserialization to private fields

I am trying to execute SOAP calls from SoapUI, against WCF, and when the XML is being deserialized, it is trying to deserialize to private fields. Why is that happening and how can I get around it? The code is listed below:

I generated POCO classes from XSD files using the standard XSD.exe and the results look something like this:

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.81.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://iec.ch/TC57/2011/MeterConfig#")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://iec.ch/TC57/2011/MeterConfig#", IsNullable = false)]
public partial class MeterConfig
{

    private ComFunction[] comFunctionField;

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("ComFunction")]
    public ComFunction[] ComFunction
    {
        get { return this.comFunctionField; }
        set { this.comFunctionField = value; }
    }
}

I have a WCF SOAP endpoint as follows:

[ServiceContract]
public class MyApi
{
    [OperationContract]
    public void CreateMeterConfig2(MeterConfig Payload)
    {
        //do nothing
    }
}

I have a SoapUI test project where I provide the following XML:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
   <soapenv:Header/>
   <soapenv:Body>
      <tem:CreateMeterConfig2>
         <tem:Payload>

         </tem:Payload>
      </tem:CreateMeterConfig2>
   </soapenv:Body>
</soapenv:Envelope>

The error I get is:

Expecting element 'comFunctionField'

Or in full:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
   <s:Body>
      <s:Fault>
         <faultcode xmlns:a="http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher">a:DeserializationFailed</faultcode>
         <faultstring xml:lang="en-ZA">The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://tempuri.org/:Payload. The InnerException message was 'Error in line 13 position 24. 'EndElement' 'Payload' from namespace 'http://tempuri.org/' is not expected. Expecting element 'comFunctionField'.'.  Please see InnerException for more details.</faultstring>
      </s:Fault>
   </s:Body>
</s:Envelope>

Upvotes: 2

Views: 743

Answers (1)

dbc
dbc

Reputation: 116980

Your problem is that you auto-generated a type marked with XmlSerializer-specific attributes but are using WCF which uses DataContractSerializer by default. When DataContractSerializer tries to serialize a type marked with [SerializableAttribute] but no data contract attributes, it infers a contract similar to how BinaryFormatter works, namely that public and private fields should be serialized, not properties. (For more, see Types Supported by the Data Contract Serializer.)

To work around this problem, you can:

  1. Configure WCF to use XmlSerializer by applying [XmlSerializerFormatAttribute] to your service. To do this, see How to change Wcf to use a different serializer? and Using the XmlSerializer Class, e.g.:

    [ServiceContract]
    [XmlSerializerFormat]
    public class MyApi
    {
        [OperationContract]
        public void CreateMeterConfig2(MeterConfig Payload)
        {
            //do nothing
        }
    }
    
  2. Automatically generate your classes from your XSD with data contract attributes rather than XmlSerializer attributes by using svcutil.exe rather than xsd.exe. To do this, see Generate DataContract from XSD and ServiceModel Metadata Utility Tool (Svcutil.exe).

    Note that DataContractSerializer has some limitations compared to XmlSerializer. For instance it doesn't allow for properties to be selectively serialized as XML attributes rather than elements. For more, see XML Serialisation - When To Use DataContractSerializer / Binary / XMLSerialiser or Data Contract Serializer - How to omit the outer element of a collection. If your XSD is not compatible with these limitations you will need to use XmlSerializer.

Upvotes: 5

Related Questions