miércoles, 6 de febrero de 2013

Recuperar un valor de un elemento de un XDocument

Que XML no es santo de mi devoción, como tampoco lo es JavaScript, es algo que destila en todo el blog. Y no lo es ninguna de las dos tecnologías por la misma razón, no son nada amigables en su aprendizaje.

Así me encuentro con la siguiente respuesta de un Webservice, que es básicamente un XmlElement con el body del mensaje SOAP en el innerXML.

<soap:Body xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <reactiveSecureClientResponse xmlns="http://irdeto.com/pisys/secureclient">
    <reactiveSecureClientResult>
      <code>1028</code>
      <message>OK</message>

    </reactiveSecureClientResult>
  </reactiveSecureClientResponse>
</soap:Body>

Del cual quiero extraer el valor de los elementos hijos code y message, y que he resaltado en negrita.

Pues bién, llevo casi 6 horas probando mil métodos y la primera aproximación, antes de refactorizarlo – y que va a ser otro artículo – es un tanto extravagante:

Primero convierto el XmlElement en un XDocument (cuidadín con liarte y utilizar XmlDocument, que se montan unos líos de aupa si mezclamos Linq to SQL con XmlDocumentde .NET).

XDocument documento = XDocument.Parse(cuerpoDelMensajeSoap.InnerXml);

¿Porqué XDocument.Parse() en vez de el conocido documento.LoadXml()? Ni idea, pero creo que es para joder. Porque ya me contarás porqué algo tan lógico como lo segundo lo cambian por algo tan raro (un constructor estático con parámetros) como lo primero.

A continuación tiro de las capacidades de Linq to XML para hacer una búsqueda que, personalmente, me parece rara de narices:

XElement code = (from XElement xmlElemento in documento.Descendants()
                       
where xmlElemento.Name.LocalName.Equals("code")
                       
select xmlElemento).FirstOrDefault();

El primer problemón que me encontré es que, a pesar de lo que dicta el sentido común, los xmlElemento que obtengo de los descendientes del XDocument (documento.Descendants()) componen su nombre con dos partes diferenciadas: el namespace y el nombre del nodo en sí.

<{http://irdeto.com/pisys/secureclient}code>1028</code>

Osea, que no solo hay que buscar por el Name del elemento, si no también por su nombre Local.

where xmlElemento.Name.LocalName.Equals("code")

Y así, y entonces si, podemos cargar el valor del nodo de marras que quería recuperar. Por cierto, te aconsejo comprobar primero si el XElement no es nulo para evitar una excepción por objeto no instanciado al querer recuperar el Value.

string codigo = (code != null) ? code.Value : string.Empty;

 

Ahora a refactorizar, que me “huele mal” cuando me tengo que traer dos valores,

public string ExtraerRespuestaDelBody(XmlElement cuerpoDelMensajeSoap)
        {

           
XDocument documento = XDocument.Parse(cuerpoDelMensajeSoap.InnerXml);

           
XElement code = (from XElement xmlElemento in documento.Descendants()
                       
where xmlElemento.Name.LocalName.Equals("code")
                       
select xmlElemento).FirstOrDefault();


           
string codigo = (code != null) ? code.Value : string.Empty;
 
           
return codigo;
        }

No hay comentarios: