При вызове API городов он возвращает этот ответ в формате XML. Я хочу десериализовать этот XML в объект. Десериализация захватывает объекты до diffgram, внутри diffgram у нас есть свойство NewDataSet
, которое возвращает null, и мне нужно получить города.
Ответ API:
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soap:Body>
<getRTLCitiesResponse xmlns="http://track.smsaexpress.com/secom/">
<getRTLCitiesResult>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="NewDataSet">
<xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="RetailCities">
<xs:complexType>
<xs:sequence>
<xs:element name="routCode" type="xs:string" minOccurs="0" />
<xs:element name="rCity" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>
<diffgr:diffgram xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<NewDataSet xmlns="">
<RetailCities diffgr:id="RetailCities1" msdata:rowOrder="0">
<routCode>ABT</routCode>
<rCity>Aqiq</rCity>
</RetailCities>
<RetailCities diffgr:id="RetailCities2" msdata:rowOrder="1">
<routCode>ABT</routCode>
<rCity>Atawlah</rCity>
</RetailCities>
<RetailCities diffgr:id="RetailCities3" msdata:rowOrder="2">
<routCode>ABT</routCode>
<rCity>Baha</rCity>
</RetailCities>
</NewDataSet>
</diffgr:diffgram>
</getRTLCitiesResult>
</getRTLCitiesResponse>
</soap:Body>
</soap:Envelope>
Подробности кода:
[XmlRoot(ElementName = "RetailCities")]
public class RetailCity
{
[XmlElement(ElementName = "routCode")]
public string RoutCode { get; set; }
[XmlElement(ElementName = "rCity")]
public string RCity { get; set; }
}
[XmlRoot(ElementName = "NewDataSet")]
public class NewDataSet
{
[XmlElement(ElementName = "RetailCities")]
public List<RetailCity> RetailCities { get; set; } = new List<RetailCity>();
}
[XmlRoot(ElementName = "diffgram", Namespace = "urn:schemas-microsoft-com:xml-diffgram-v1")]
public class Diffgram
{
[XmlElement(ElementName = "NewDataSet")]
public NewDataSet NewDataSet { get; set; }
}
[XmlRoot(ElementName = "getRTLCitiesResult", Namespace = "http://track.smsaexpress.com/secom/")]
public class GetRTLCitiesResult
{
[XmlElement(ElementName = "diffgram", Namespace = "urn:schemas-microsoft-com:xml-diffgram-v1")]
public Diffgram Diffgram { get; set; }
}
[XmlRoot(ElementName = "getRTLCitiesResponse", Namespace = "http://track.smsaexpress.com/secom/")]
public class GetRTLCitiesResponse
{
[XmlElement(ElementName = "getRTLCitiesResult")]
public GetRTLCitiesResult GetRTLCitiesResult { get; set; }
}
[XmlRoot(ElementName = "Body", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public class SoapBody
{
[XmlElement(ElementName = "getRTLCitiesResponse", Namespace = "http://track.smsaexpress.com/secom/")]
public GetRTLCitiesResponse GetRTLCitiesResponse { get; set; }
}
[XmlRoot(ElementName = "Envelope", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public class SoapEnvelope
{
[XmlElement(ElementName = "Body")]
public SoapBody Body { get; set; }
[XmlNamespaceDeclarations]
public XmlSerializerNamespaces Xmlns { get; set; } = new XmlSerializerNamespaces();
public SoapEnvelope()
{
Xmlns.Add("soap", "http://schemas.xmlsoap.org/soap/envelope/");
Xmlns.Add("xsi", "http://www.w3.org/2001/XMLSchema-instance");
Xmlns.Add("xsd", "http://www.w3.org/2001/XMLSchema");
}
}
private static T DeserializeXml<T>(string xml)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (StringReader reader = new StringReader(xml))
{
return (T)serializer.Deserialize(reader)!;
}
}
Вызов API и десериализация XML-ответа:
using (var client = new HttpClient())
{
var content = new StringContent(AramexCitiesSoapEnvelope(), Encoding.UTF8, "text/xml");
try
{
var response = await client.PostAsync(endPoint, content);
if (response.IsSuccessStatusCode)
{
// Прочитать и вернуть содержимое ответа
var resultContent = await response.Content.ReadAsStringAsync();
var envelope = DeserializeXml<SoapEnvelope>(resultContent);
return envelope;
}
else
{
// Вернуть содержимое ответа с ошибкой
var errorContent = await response.Content.ReadAsStringAsync();
return new SoapEnvelope() { ErrorMessage = errorContent };
}
}
catch (HttpRequestException e)
{
return new SoapEnvelope() { ErrorMessage = e.Message };
}
}
Результат:
Я создал классы модели и украсил эти модели аннотациями XmlElement
, чтобы десериализовать XML-ответ и получить названия городов, которые находятся внутри списка RetailCities
. Тем не менее, к сожалению, мне не удается добраться до RetailCities
, так как NewDataSet
возвращает null.
Из вашего XML <NewDataSet xmlns="">
следует добавить Namespace = ""
в атрибут XmlElement
для свойства NewDataSet
в классе Diffgram
.
[XmlRoot(ElementName = "diffgram", Namespace = "urn:schemas-microsoft-com:xml-diffgram-v1")]
public class Diffgram
{
[XmlElement(ElementName = "NewDataSet", Namespace = "")]
public NewDataSet NewDataSet { get; set; }
}
Ответ
Чтобы десериализовать ответ XML от API городов, необходимо убедиться, что ваши классы модели правильно соответствуют структуре XML. В вашем случае проблема возникает из-за отсутствия пространства имен для элемента NewDataSet
, который у вас в XML выглядит так: <NewDataSet xmlns="">
.
Вы уже сделали большую работу, создав классы для десериализации, но вам нужно внести одно изменение:
Изменение в классе Diffgram
Добавьте пространство имен в атрибут XmlElement
для свойства NewDataSet
классa Diffgram
. Это необходимо, поскольку в XML у элемента NewDataSet
задано пустое пространство имен (xmlns=""
), поэтому ваш класс должен это учитывать.
Вот исправленный класс Diffgram
:
[XmlRoot(ElementName = "diffgram", Namespace = "urn:schemas-microsoft-com:xml-diffgram-v1")]
public class Diffgram
{
[XmlElement(ElementName = "NewDataSet", Namespace = "")]
public NewDataSet NewDataSet { get; set; }
}
Дополнительные шаги для десериализации
После того как вы внесете это исправление, ваше существующее решение по десериализации должно работать правильно. После получения ответа API и его чтения, вы сможете извлечь список городов следующим образом:
using (var client = new HttpClient())
{
var content = new StringContent(AramexCitiesSoapEnvelope(), Encoding.UTF8, "text/xml");
try
{
var response = await client.PostAsync(endPoint, content);
if (response.IsSuccessStatusCode)
{
// Чтение и возврат содержимого ответа
var resultContent = await response.Content.ReadAsStringAsync();
var envelope = DeserializeXml<SoapEnvelope>(resultContent);
// Получение списка городов
var cities = envelope.Body.GetRTLCitiesResponse.GetRTLCitiesResult.Diffgram.NewDataSet.RetailCities;
return cities; // Возвращает список городов
}
else
{
// Возврат содержимого неудачного ответа
var errorContent = await response.Content.ReadAsStringAsync();
return new SoapEnvelope() { ErrorMessage = errorContent };
}
}
catch (HttpRequestException e)
{
return new SoapEnvelope() { ErrorMessage = e.Message };
}
}
Заключение
После внесения вышеуказанных изменений, ваша программа должна корректно десериализовать ответ API. Обратите внимание на то, что правильные пространства имен являются ключевыми в XML-десериализации. Убедитесь, что у вас правильные атрибуты в классах, чтобы ссылаться на необходимые части XML. Теперь вы сможете получить доступ к списку городов, находящемуся внутри RetailCities
.