Search code examples
c#xmlwcfsoapxml-deserialization

Exception de-serializing SOAP XML into Object


I am developing a WCF Service that can accept and return SOAP 1.1 messages. However, I am having problems deserializing the SOAP body to my object. At the moment I have;

namespace MyAPI
{    
    [ServiceContract]
    public interface IMyService
    {
        [OperationContract(IsOneWay = false, Action = "*", ReplyAction = "*")]     
        [WebInvoke(Method = "POST",           
           BodyStyle = WebMessageBodyStyle.Bare, RequestFormat=WebMessageFormat.Xml)]
        Message ProcessMessgae(Message message);       
    }
}

public class MyService : IMyService
{  
    public Message ProcessMessgae(Message message)
    {
        MessageBuffer buffer = message.CreateBufferedCopy(8192);

        // Get a copy of the original message.
        Message msgCopy = buffer.CreateMessage();

        // Take another copy of the same message. 
        Message returnMsg = buffer.CreateMessage();

        // Use the msgCopy to get an XML and extract the body contents. Once this message has been read and consumed,

        System.Xml.XmlDictionaryReader xrdr = msgCopy.GetReaderAtBodyContents();

        string bodyData = xrdr.ReadOuterXml();

        var reader = new StringReader(bodyData);
        var serializer = new XmlSerializer(typeof(OTA_HotelAvailRQ));
        var instance = (OTA_HotelAvailRQ)serializer.Deserialize(reader);

        return returnMsg; 
    }
}

This is my auto-generated object

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.7.2046.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://www.opentravel.org/OTA/2003/05")]
public partial class OTA_HotelAvailRQ : object, System.ComponentModel.INotifyPropertyChanged
{

    private string echoTokenField;

    private System.DateTime timeStampField;

    private bool timeStampFieldSpecified;

    private bool targetFieldSpecified;

    private decimal versionField;

    private bool availRatesOnlyField;

    private bool availRatesOnlyFieldSpecified;

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string EchoToken
    {
        get
        {
            return this.echoTokenField;
        }
        set
        {
            this.echoTokenField = value;
            this.RaisePropertyChanged("EchoToken");
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public System.DateTime TimeStamp
    {
        get
        {
            return this.timeStampField;
        }
        set
        {
            this.timeStampField = value;
            this.RaisePropertyChanged("TimeStamp");
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlIgnoreAttribute()]
    public bool TimeStampSpecified
    {
        get
        {
            return this.timeStampFieldSpecified;
        }
        set
        {
            this.timeStampFieldSpecified = value;
            this.RaisePropertyChanged("TimeStampSpecified");
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public decimal Version
    {
        get
        {
            return this.versionField;
        }
        set
        {
            this.versionField = value;
            this.RaisePropertyChanged("Version");
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public bool AvailRatesOnly
    {
        get
        {
            return this.availRatesOnlyField;
        }
        set
        {
            this.availRatesOnlyField = value;
            this.RaisePropertyChanged("AvailRatesOnly");
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlIgnoreAttribute()]
    public bool AvailRatesOnlySpecified
    {
        get
        {
            return this.availRatesOnlyFieldSpecified;
        }
        set
        {
            this.availRatesOnlyFieldSpecified = value;
            this.RaisePropertyChanged("AvailRatesOnlySpecified");
        }
    }

    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

    protected void RaisePropertyChanged(string propertyName)
    {
        System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
        if ((propertyChanged != null))
        {
            propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
        }
    }
}

However, when I run the code I'm getting an exception on the following line var instance = (OTA_HotelAvailRQ)serializer.Deserialize(reader);

The SOAP XML I'm posting is;

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Header xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <wsse:Security soap:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <wsse:UsernameToken> <wsse:Username>test</wsse:Username> <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">thebest</wsse:Password> </wsse:UsernameToken> </wsse:Security> </SOAP-ENV:Header> <SOAP-ENV:Body xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <OTA_HotelAvailRQ xmlns="http://www.opentravel.org/OTA/2003/05" Version="1.0" TimeStamp="2005-08-01T09:30:47+02:00" EchoToken="fb57388d" AvailRatesOnly="true"> </OTA_HotelAvailRQ> </SOAP-ENV:Body> </SOAP-ENV:Envelope>

The actual exception is;enter image description here


Solution

  • What happens if you add your namespace to the xmlserializer?

    var instance = (OTA_HotelAvailRQ)serializer.Deserialize(reader, "http://www.opentravel.org/OTA/2003/05");
    

    Another option would be do something like the below, you may have to ReadOuterXml() instead of ReadInnerXml, the namespace may be able to be removed from this method too:

    System.Xml.Linq.XElement body = System.Xml.Linq.XElement.Parse(msgCopy.GetReaderAtBodyContents().ReadInnerXml());
    var instance = Deserialize<OTA_HotelAvailRQ>(body, "http://www.opentravel.org/OTA/2003/05");
    
    public static T Deserialize<T>(XElement xElement, string nameSpace)
    {
        using (MemoryStream memoryStream = new MemoryStream(Encoding.ASCII.GetBytes(xElement.ToString())))
        {
            XmlSerializer xmlSerializer = new XmlSerializer(typeof(T), nameSpace);
            return (T)xmlSerializer.Deserialize(memoryStream);
        }
    }
    

    If you are still having problems, there is also a way using the DataContractSerializer or a SoapreflectionImporter, but you don't have a very complex object there those may be overkill.