Я получил вариант 2, который я упомянул в своем исходном вопросе, чтобы работать: (Это не полный пример, но должно быть достаточно очевидно, что вам нужно изменить, чтобы он работал в вашей ситуации, также отмечая эту вики, чтобы любой может упростить это в будущем)
Решение, описанное здесь: Создание запроса на мыло вручную, но с использованием всех сгенерированных классов wsdl.exe для десериализации и хранения данных после обработки ответа.
- Не простое решение, но оно гораздо более мягкое, чем использование сгенерированных вызовов wsdl.exe, и любые методы, использующие сгенерированные классы, будут по-прежнему работать идеально
- Я считаю, что он способен загружать любые элементы, общие для целевого объекта и исходного ответа, поэтому, если новые версии только добавляют поля:
- загрузка из более новой версии будет иметь все данные, кроме новых полей
- загрузка из более старой версии, новые поля будут нулевыми
- Еще одним преимуществом является то, что вы можете загружать и XML-строку из любого места (чтение с диска, загрузка на веб-страницу) в
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);
Затем запишите в этот поток данные xml для веб-метода: Это основной недостаток этого метода, я пока не нашел надежного способа создания мыльного конверта, для моего сервиса это не похоже заботиться о версии, указанной в 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
извлекает XmlArray, который я хочу десериализовать из элементов soapenv:Envelope
, и помещает его в новый XmlDocument, и я преобразую его обратно в строку.
Таким образом, после NormalizeSummaryVersion
и внутри GetData()
XmlDocument processedDoc
будет этот xml, независимо от того, от какой версии был ответ Soap:
<?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();
}