Reputation: 11
I succeeded in building a WCF client generated by svcutil.exe from the WSDL. Using the generated client proxy class I can call the web service of an external service supplier. I also succeeded in coding a message inspector, as I need to log both raw XML request and response as full SOAP message to the database.
For an emergency scenario I also need to be able to "import" a raw XML response. I found many hints on using XMLSerializer
or deserializing WCF messages based on the message contract.
But how can I deserialize a raw XML response based on an operation contract? For a first test I use one of the logged raw responses, save it to a file and now try to deserialize it to the response type as generated in the client proxy. Somehow I must succeed in calling DeserializeReply()
from class ClientOperation
. But how to get there?
I happily accept any help as I'm quite new to WCF... TIA, Stefan
This is what I tried after Marc's answer:
public static RatingResult DeserializeResponseFromFile(string path)
{
var xmlReader = XmlReader.Create(path);
var message = Message.CreateMessage(xmlReader, int.MaxValue, MessageVersion.Soap11);
var readerAtBodyContents = message.GetReaderAtBodyContents();
var dcs = new DataContractSerializer(typeof(RatingResult), "RatingResponse", "http://rating.webservice.xxx.de");
// Error in line 6 position 7. 'EndElement' 'RatingResponse' from namespace
// 'http://rating.webservice.xxx.de' is not expected.
// Expecting element 'commonDataField'.
var wsResult = (RatingResult)dcs.ReadObject(readerAtBodyContents);
return wsResult;
}
This is part of the logged XML response file, that I'm trying to deserialize to type RatingResponse
:
<soapenv:Envelope xmlns:soapenv="..." xmlns:soapenc="..." xmlns:xsd="..." xmlns:xsi="...">
<soapenv:Header soapenv:encodingStyle="..." />
<soapenv:Body soapenv:encodingStyle="...">
<p933:RatingResponse xmlns:p933="http://rating.webservice.xxx.de">
<RatingReturn href="#id0" />
</p933:RatingResponse>
<multiRef id="id0" soapenc:root="0" soapenv:encodingStyle="..." xsi:type="p878:RatingResult" xmlns:p878="http://output.rating.webservice.xxx.de">
<commonData href="#id1" />
<acctData href="#id2" />
<resultData href="#id3" />
</multiRef>
<multiRef id="id1" soapenc:root="0" soapenv:encodingStyle="..." xsi:type="p719:RatingCommonData" xmlns:p719="http://input.rating.webservice.xxx.de">
<requestdate xsi:type="xsd:dateTime">2010-12-24T09:45:09.531Z</requestdate>
...
I guess that the data contract serializer has problems deserializing the href's. Please note that the message I try to deserialize "by hand" was captured using my injected message inspector. In a "normal" call of the web service this message get deserialized without problems.
Upvotes: 1
Views: 6090
Reputation: 1829
For anyone in the future doing this. I had to manually read a WCF message out of the MSMSQ, and get the request object out of the MSMQ/WCF message envelope. Here's how:
Root code:
var q = new MessageQueue(@".\Private$\VishalQ;poison");
var allMessages = q.GetAllMessages().ToList();
var wcfRequests = allMessages.Select(ConvertToWcfRequest<ObjectChangedRequest>);
My contract:
[ServiceContract]
public interface IWish
{
[OperationContract(IsOneWay = true)]
void ObjectChanged(ObjectChangedRequest request);
}
My Data Contract:
[DataContract(Namespace = "http://x.namespaces.x-x.com/")]
public class ObjectChangedRequest
{
[DataMember]
public OperationType OperationType { get; set; }
}
My message deserialization code:
/// <summary>
/// Converts a WCF MSMQ message to a WCF request object.
/// </summary>
public static T ConvertToWcfRequest<T>(Message msmqMessage)
{
var buffer = new byte[msmqMessage.BodyStream.Length];
msmqMessage.BodyStream.Read(buffer, 0, (int)msmqMessage.BodyStream.Length);
var envelopeStart = FindEnvelopeStart(buffer);
using var msmqStream = new MemoryStream(buffer, envelopeStart, buffer.Length - envelopeStart);
var encodingElement = new BinaryMessageEncodingBindingElement();
var wcfMessage = encodingElement.CreateMessageEncoderFactory().Encoder.ReadMessage(msmqStream, int.MaxValue);
var document = new XmlDocument();
document.Load(wcfMessage.GetReaderAtBodyContents());
var realRoot = document.FirstChild.FirstChild;
using var wcfStream = new MemoryStream();
using var xmlWriter = XmlWriter.Create(wcfStream);
realRoot.WriteTo(xmlWriter);
xmlWriter.Flush();
wcfStream.Seek(0, SeekOrigin.Begin);
var wcfSerializer = new DataContractSerializer(typeof(T), realRoot.Name, "http://tempuri.org/"); //No idea why this has to be temp uri and not our namespace...
return (T)wcfSerializer.ReadObject(wcfStream);
}
/// <summary>
/// Locates the start of a WCF message within a MSMQ message.
/// </summary>
private static int FindEnvelopeStart(byte[] stream)
{
var position = 0;
var previousByte = stream[position];
for (position = 0; position < stream.Length; position++)
{
var currentByte = stream[position];
//Some magic numbers that define the start of the WCF message envelope
if (currentByte == 0x02 && previousByte == 0x56)
break;
previousByte = currentByte;
}
return position - 1;
}
Upvotes: 1
Reputation: 755321
I don't really understand what you're trying to ask and to do.... based on an operation contract ?? The operation contract is just an attribute you put on an operation / method call to mark it as a service method .... the operation contract doesn't do anything even remotely involved with serialization or deserialization..... do you mean how to deserialize an XML message using the DataContractSerializer
which is the WCF default serializer??
Assuming you do really mean HOWTO: deserialize a WCF message using the DataContractSerializer, then try this: if you have the response XML from a service call that used the default WCF DataContractSerializer
, you should be able to deserialize it like this (assuming you have your XML serialized response in a xmlResponse
variable):
using(MemoryStream memStm = new MemoryStream())
using(StreamWriter stw = new StreamWriter(memStm))
{
// write your response to the memory stream
stw.Write(xmlResponse);
stw.Flush();
// "reset" memory stream
memStm.Seek(0, SeekOrigin.Begin);
// setup DataContractSerializer
DataContractSerializer dcs = new DataContractSerializer(typeof(YourDataType));
// deserialize result XML into an instance of "YourDataType"
var result = dcs.ReadObject(memStm);
}
Upvotes: 3