lunes, 21 de octubre de 2013

Navegar por un Xdocument con Linq to XML

Hay cosas que no es que sean tontas, si no que son tan, tan sencillas que en ningún lado las publican porque es de nivel 100 o menor.

Y aquí es una de ellas que me ha hecho perder un par de horas: Navegar por el esquema de un Xdocument para acceder a la primer nodo que está dentro de un array, que a su vez está en un esquema mayor. Y que, es en donde está la dificultad, se llama igual que el nodo de otro array.

Algo así:

                   <ListOfDatos'>
                        <Datos_Telefonos>
                           <Datos_Telefonos>
                              <Telefono>658965645</Telefono>
                           </Datos_Telefonos>
                           <Datos_Telefonos>
                              <Telefono>983983983</Telefono>
                           </Datos_Telefonos>
                        </Datos_Telefonos>
                     </ListOfDatos_Telefonos>

                     <ListOfDatos_Telefono_Movil>
                        <Datos_Telefono_Movil>
                           <Datos_Telefono_Movil>
                              <Telefono>677448137</Telefono>
                           </Datos_Telefono_Movil>
                        </Datos_Telefono_Movil>
                     </ListOfDatos_Telefono_Movil>

Que a su vez está dentro de un esquema mucho mayor con, al menos, 6 niveles por encima.

Lo primer es que Linq to XML es una maravilla y me permite acceder directamente al nodo que quiero situar como punto de partida, sin navegación XPath ni horrores similares:

var elemento= (from XElement xmlElemento in xmlDocument.Descendants("Datos_Telefonos")
                                                                                         .Descendants(
"Telefono")
           
select xmlElemento).FirstOrDefault();

Así le puedo indicar que me traiga los descendientes (un IENumerable de XElement) desde el nodo “Datos_Telefonos”, y que a partir de allí me traiga todos los nodos descendientes con el nombre “Telefono”.

Y como solo quiero el primero (si, es un requisito muy raro, pero real), le hago un FirstOrDefault, y ya tengo mi XElement del que sacar, por ejemplo, el Value.

Un último consejo, como puede ser que el Xdocument varié en el tiempo, sería bueno ponerle un control de null a la variable elemento, para que algo como elemento.Value no produzca una rotura, al no existir ese nombre de nodo en el esquema.

Espero que sea útil.

viernes, 18 de octubre de 2013

Introducir contenido de un XDocument a un XmlReader, en una deserealización

Tengo un Xdocument que contiene la respuesta XML de un webservice. Además tengo una clase empleado que tiene los campos y los decoradores adecuados al esquema para poder hacer una deserialización directa.

Y entonces me veo en el marrón de siempre que tengo que trabajar con stream, el método Deserialize requiere que trabaje con un XmlReader, que en sus tropecientas sobrecargas parte de un stream que, como buen IO, está diseñado principalmente para ser utilizado con un fichero físico.

Después de un buen rato de desesperación, el intellisense me ha dado la solución más simple y obvia, y es que el objeto de tipo XDocument tiene un método llamado CreateReader() que me devuelve un XmlReader…

        public Empleado ToEmpleado(XDocument xmlDocument)
        {
           
var empleado = new Empleado();

           
XmlSerializer serializer = new XmlSerializer(typeof(Empleado));
            empleado = (
Empleado)serializer.Deserialize(xmlDocument.CreateReader());

            return empleado;       
        }

Espero que sea de utilidad.

jueves, 17 de octubre de 2013

Limitar el tamaño de una propiedad del tipo string

La verdad que tengo mucha suerte de tener tanta gente realmente buena en el TimeLine de mi Twiter.

Así cuando lance la pregunta que es el título de este artículo, recibí ayuda e ideas de todo tipo que me han ayudado a llegar a alguna conclusión.

Y esa es que, no se puede de forma sencilla.

Los test que debían pasar eran dos y muy sencillos:

       [TestMethod]
      
public void Introducir_en_NIF_mas_de_9_caracteres()
        {
          
var empleado =newEmpleado();
          
var nif ="123456789012";

            empleado.NIF = nif;

          
Assert.AreEqual(empleado.NIF,"123456789");
           
        }
        [
TestMethod]
      
public void Introducir_en_NIF_5_caracteres()
        {
          
var empleado =newEmpleado();
           
var nif = "12345";

            empleado.NIF = nif;

           
Assert.AreEqual(empleado.NIF, "12345");

        }

Eduard Tomás ha realizado un precioso ejercicio en donde, por medio de conversiones explícitas y genéricos, consigue el objetivo. Eso sí, al coste de jartarte de escribir código. Mira el impresionante artículo que ha publicado en su blog.

También Javier Torrecilla y Juan José Luis Garcí me recomendaron utilizar descriptores/decoradores de la propiedad que están en el namespace de System.Component.ModelDataAnnotations. el problema es que el objetivo es una clase de una entidad que no tiene fachada de interface de usuario, para lo que es estas anotaciones.

[StringLength(9)]
public string NIF { get; set; }

Esto no pasó los test.

Por otro lado Nicolás Herrera me indicó que podría utilizar StringBuilder para la variable privada que utilice la propiedad, y que me permite limitar el tamaño.

private StringBuilder _int = new StringBuilder(9,9);
public string NIF { get { return _int.ToString(); } set { _int.Append(value.ToString()); } }

Lo cual consigue que pase perfectamente el segundo test, pero en el primero me rompe porque StringBuilder me lanza una excepción al intentar meter un valor mayor de lo permitido.

Como soy un agonías y no me gusta la programación basada en excepciones, pues también lo he desechado.

Y así volví a mi implementación original, que también me ha recomendado Marcos Rodríguez, Pedro Herrarte y Juan María Hernández, y que se trata de introducir la lógica para realizar el truncado en el set de la propiedad.

Así lo primero que hago es hacer un método dentro de la clase de la entidad que se va a ocupar de truncar la cadena. Y no cambiar nada, si tiene la longitud máxima o menos.

        private string CutTo(string cadena, int tamañoMaximo)
        {
           
if (cadena.Length > tamañoMaximo)
            { cadena = cadena.Substring(0, tamañoMaximo); }
           
           
return cadena;
        }

Y ahora sí, mi propiedad queda así:

        private string _int;
       
public string NIF { get { return _int; } set { _int = CutTo(value, 9); } }

Y esto si que me pasa los dos test con un hermoso verde.

Muchas gracias a todos los que me habéis aconsejado, este post va por vosotros CRACKS!!