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>
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.