我正在使用其他公司的网络服务,他们有多个版本在运行,每个新版本只添加了新的字段/对象,但是更改了一些元素名称。
我希望能够使用具有相同代码的任何版本。
具体在一个版本中,搜索方法返回:
<searchReturn><SummaryData_Version1Impl /><SummaryData_Version1Impl /></searchReturn>
并使用其他版本:<searchReturn><SummaryData_Version2Impl /><SummaryData_Version2Impl /></searchReturn>
所以现在由wsdl.exe生成的代理因为元素更改而无法同时使用它们。
class Data3 : IData3, IData2, IData1
的接口,我认为这样可以让我至少向下抛出。并将每个版本放入不同的命名空间。答案 0 :(得分:1)
没有办法做到这一点。不同的版本是不同的。没有办法提前知道它们有多相似。
答案 1 :(得分:1)
我在原始问题中提到的选项2可以工作:(这不是一个完整的例子,但应该相当明显,你需要修改它以使它在你的情况下工作,也标记这个wiki所以任何人可以在将来简化这一点)
解决方案在此处描述:手动制作Soap Request,但在处理完回复后,使用所有wsdl.exe生成的类来反序列化并保存数据。
NormalizeSummaryVersion
,其余的过程将完全相同,从而使得对象具有兼容性。 设置WebRequest是这样的:(我的是具有基本身份验证的https网络服务,无法使req.Credentials
正常工作,因此我手动添加该标头)
WebRequest req = WebRequest.Create(url);
req.Headers.Add("SOAPAction", soapAction);
req.ContentType = "text/xml;";
req.Method = WebRequestMethods.Http.Post;
req.Headers.Add(HttpRequestHeader.Authorization, "Basic " + basicAuthEncoded);
然后向该流写入webmethod的xml数据:这是此方法的主要缺点,我还没有找到一种可靠的方法来生成soap信封,对于我的服务它似乎不太关心xmlns:ver
中列出的版本,因此我使用此字符串并将SerializeObject(SearchCriteria)
传递给它
//{0} is the soapAction
//{1} is the xml for that call
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ver="fake">
<soapenv:Header/>
<soapenv:Body>
<ver:{0}>
{1}
</ver:{0}>
</soapenv:Body>
</soapenv:Envelope>
注意:以下是我的概念证明代码,我确信它可以清理并简化相当数量。
有了这个,我就能从服务中读取xml响应。接下来,我调用NormalizeSummaryVersion
重命名可能的节点名称差异,如果需要,还可以处理其中的任何其他节点或数据。
public string NormalizeSummaryVersion(string xmlString)
{
xmlString = Regex.Replace(xmlString,"SummaryData_Version2_2Impl|SummaryData_Version3_3Impl|SummaryData_Version4_4Impl",
"SummaryData_Version1_1Impl");
return xmlString;
}
所以现在节点有一个共同的名称和格式(额外或缺少的节点似乎并不重要,它只是忽略它们或使用这种反序列化方法将它们设置为默认值)
ProcessLikeService
从soapenv:Envelope
元素中提取要反序列化的XmlArray,并将其放入新的XmlDocument中,然后将其转换回字符串。
因此,NormalizeSummaryVersion
之后GetData()
XmlDocument processedDoc
内部将是此xml,无论Soap Response来自何种版本:
<?xml version="1.0" encoding="utf-16"?>
<searchReturn>
<SummaryData_Version1_1Impl>
<customerFirstName>first</customerFirstName>
<customerLastName>last</customerLastName>
</SummaryData_Version1_1Impl>
</searchReturn>
最后我能够使用通用的XmlDeserialize方法来获取我想要的对象。 (我对所有这些的主要调用实际上返回GetData(xmlString).searchReturn
,因为
[XmlRoot("searchReturn")]
public class SearchReturn
{
[XmlElement("SummaryData_Version1_1Impl", typeof(SummaryData))]
public SummaryData[] searchReturn;
}
public SearchReturn GetData(string xmlString)
{
System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
doc.LoadXml(xmlString);
System.Xml.XmlNode DataNode = doc.SelectSingleNode("//searchReturn");
System.Xml.XmlDocument processedDoc = new System.Xml.XmlDocument();
processedDoc.AppendChild(processedDoc.ImportNode(DataNode, true));
SearchReturn data = Deserialize<SearchReturn>(processedDoc);
return data;
}
通用反序列化方法:
public static T Deserialize<T>(XmlDocument xml)
{
XmlSerializer s = new XmlSerializer(typeof(T));
using (XmlReader reader = new XmlNodeReader(xml))
{
try
{
return (T)s.Deserialize(reader);
}
catch (Exception)
{
throw;
}
}
throw new NotSupportedException();
}
答案 2 :(得分:1)
我现在认为最好的选择是使用SoapExtension 并设置SoapExtensionAttribute来触发在修改响应所需的任何方法上使用它。
[ModifyResponseExtensionAttribute]
添加到需要修改的任何方法中,在您的情况下,您可能需要多个SoapExtension类将以下类添加到项目中:
public class ModifyResponseExtension : SoapExtension
{
Stream inStream;
Stream outStream;
// Save the Stream representing the SOAP request or SOAP response into
// a local memory buffer.
public override Stream ChainStream(Stream stream)
{
inStream = stream;
outStream = new MemoryStream();
return outStream;
}
//This can get properties out of the Attribute used to enable this
public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
{
return null;
}
//This would have default settings when enabled by config file
public override object GetInitializer(Type WebServiceType)
{
return null;
}
// Receive the object returned by GetInitializer-- set any options here
public override void Initialize(object initializer)
{
}
// If the SoapMessageStage is such that the SoapRequest or
// SoapResponse is still in the SOAP format to be sent or received,
// save it out to a file.
public override void ProcessMessage(SoapMessage message)
{
switch (message.Stage)
{
case SoapMessageStage.BeforeSerialize:
break;
case SoapMessageStage.AfterSerialize:
//This is after the Request has been serialized, I don't need to modify this so just copy the stream as-is
outStream.Position = 0;
Copy(outStream, inStream);
//Not sure if this is needed (MSDN does not have it) but I like closing things
outStream.Close();
inStream.Close();
break;
case SoapMessageStage.BeforeDeserialize:
//This is before the Response has been deserialized, modify here
//Could also modify based on something in the SoapMessage object if needed
ModifyResponseMessage();
break;
case SoapMessageStage.AfterDeserialize:
break;
}
}
private void ModifyResponseMessage()
{
TextReader reader = new StreamReader(inStream);
TextWriter writer = new StreamWriter(outStream);
//Using a StringBuilder for the replacements here
StringBuilder sb = new StringBuilder(reader.ReadToEnd());
//Modify the stream so it will deserialize with the current version (downgrading to Version1_1 here)
sb.Replace("SummaryData_Version2_2Impl", "SummaryData_Version1_1Impl")
.Replace("SummaryData_Version3_3Impl", "SummaryData_Version1_1Impl")
.Replace("SummaryData_Version4_4Impl", "SummaryData_Version1_1Impl");
//Replace the namespace
sb.Replace("http://version2_2", "http://version1_1")
.Replace("http://version3_3", "http://version1_1")
.Replace("http://version4_4", "http://version1_1");
//Note: Can output to a log message here if needed, with sb.ToString() to check what is different between the version responses
writer.WriteLine(sb.ToString());
writer.Flush();
//Not sure if this is needed (MSDN does not have it) but I like closing things
inStream.Close();
outStream.Position = 0;
}
void Copy(Stream from, Stream to)
{
TextReader reader = new StreamReader(from);
TextWriter writer = new StreamWriter(to);
writer.WriteLine(reader.ReadToEnd());
writer.Flush();
}
}
// Create a SoapExtensionAttribute for the SOAP Extension that can be
// applied to an XML Web service method.
[AttributeUsage(AttributeTargets.Method)]
public class ModifyResponseExtensionAttribute : SoapExtensionAttribute
{
private int priority;
public override Type ExtensionType
{
get { return typeof(ModifyResponseExtension); }
}
public override int Priority
{
get { return priority; }
set { priority = value; }
}
}
因此,在需要时可以手动修改wsdl.exe生成的类的请求/响应。