我成功地从WSDL构建了一个由svcutil.exe生成的WCF客户端。使用生成的客户端代理类,我可以调用外部服务提供者的Web服务。我还成功编写了一个消息检查器,因为我需要将原始XML请求和响应作为完整的SOAP消息记录到数据库中。
对于紧急情况,我还需要能够“导入”原始XML响应。我发现了很多关于使用XMLSerializer
或根据消息合同反序列化WCF消息的提示。
但是如何根据操作合同反序列化原始XML响应?对于第一个测试,我使用其中一个记录的原始响应,将其保存到文件中,现在尝试将其反序列化为客户端代理中生成的响应类型。不知怎的,我必须成功地从班级DeserializeReply()
拨打ClientOperation
。但是如何到达那里?
我很高兴接受任何帮助,因为我对WCF很新... TIA, 斯蒂芬
这是我在Marc回答之后尝试过的:
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;
}
这是已记录的XML响应文件的一部分,我正在尝试反序列化以键入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>
...
我猜数据契约序列化程序在反序列化href时遇到问题。请注意,我尝试使用我的注入消息检查器捕获“手动”反序列化的消息。在Web服务的“正常”调用中,此消息被反序列化而没有问题。
答案 0 :(得分:3)
我真的不明白你要求和做什么.... 基于运营合同? 操作合同只是您在操作/方法调用上将其标记为服务方法的属性....操作合同即使远程涉及序列化或反序列化也不会执行任何操作。 ...你的意思是如何使用DataContractSerializer
(即WCF默认序列化器)反序列化XML消息?
假设你的意思是 HOWTO:使用DataContractSerializer 反序列化WCF消息,那么试试这个:如果你有来自使用默认WCF DataContractSerializer
的服务调用的响应XML,您应该能够像这样反序列化它(假设您在xmlResponse
变量中有XML序列化响应):
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);
}
答案 1 :(得分:0)
对于将来这样做的任何人。我必须手动从MSMSQ中读取WCF消息,并从MSMQ / WCF消息信封中获取请求对象。方法如下:
根代码:
var q = new MessageQueue(@".\Private$\VishalQ;poison");
var allMessages = q.GetAllMessages().ToList();
var wcfRequests = allMessages.Select(ConvertToWcfRequest<ObjectChangedRequest>);
我的合同:
[ServiceContract]
public interface IWish
{
[OperationContract(IsOneWay = true)]
void ObjectChanged(ObjectChangedRequest request);
}
我的数据合同:
[DataContract(Namespace = "http://x.namespaces.x-x.com/")]
public class ObjectChangedRequest
{
[DataMember]
public OperationType OperationType { get; set; }
}
我的邮件反序列化代码:
/// <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;
}