ASP.NET Core – Не удается десериализовать XML-ответ API городов

Вопросы и ответы

При вызове 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.

Оцените материал
Добавить комментарий

Капча загружается...