Inicio Android Tratamiento de XML en Android (III): DOM

Tratamiento de XML en Android (III): DOM

por sgoliver

En el artículo anterior del curso de programación para Android hablamos sobre SAX, el primero de los métodos disponibles en Android para leer ficheros XML desde nuestras aplicaciones. En este segundo artículo vamos a centrarnos en DOM, otro de los métodos clásicos para la lectura y tratamiento de XML.

Cuando comentábamos la filosofía de SAX ya vimos cómo con dicho modelo el tratamiento del fichero XML se realizaba de forma secuencial, es decir, se iban realizando las acciones necesarias durante la propia lectura del documento. Sin embargo, con DOM la estrategia cambia radicalmente. Con DOM, el documento XML se lee completamente antes de poder realizar ninguna acción en función de su contenido. Esto es posible gracias a que, como resultado de la lectura del documento, el parser DOM devuelve todo su contenido en forma de una estructura de tipo árbol, donde los distintos elementos del XML se representa en forma de nodos y su jerarquía padre-hijo se establece mediante relaciones entre dichos nodos.

Como ejemplo, vemos un ejemplo de XML sencillo y cómo quedaría su representación en forma de árbol:

<noticias>
    <noticia>
        <titulo>T1</titulo>
        <link>L1</link>
    </noticia>
    <noticia>
        <titulo>T2</titulo>
        <link>L2</link>
    </noticia>
<noticias>

Este XML se traduciría en un árbol parecido al siguiente:

Árbol XML - DOM

Como vemos, este árbol conserva la misma información contenida en el fichero XML pero en forma de nodos y transiciones entre nodos, de forma que se puede navegar fácilmente por la estructura. Además, este árbol se  conserva persistente en memoria una vez leido el documento completo, lo que permite procesarlo en cualquier orden y tantas veces como sea necesario (a diferencia de SAX, donde el tratamiento era secuencial y siempre de principio a fin del documento, no pudiendo volver atrás una vez finalizada la lectura del XML).

Para todo esto, el modelo DOM ofrece una serie de clases y métodos que permiten almacenar la información de la forma descrita y facilitan la navegación y el tratamiento de la estructura creada.

Veamos cómo quedaría nuestro parser utilizando el modelo DOM y justo después comentaremos los detalles más importantes.

public class RssParserDom
{
	private URL rssUrl;

	public RssParserDom(String url)
	{
		try
		{
			this.rssUrl = new URL(url);
		}
		catch (MalformedURLException e)
		{
			throw new RuntimeException(e);
		}
	}

	public List<Noticia> parse()
	{
		//Instanciamos la fábrica para DOM
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		List<Noticia> noticias = new ArrayList<Noticia>();

		try
		{
			//Creamos un nuevo parser DOM
			DocumentBuilder builder = factory.newDocumentBuilder();

			//Realizamos lalectura completa del XML
			Document dom = builder.parse(this.getInputStream());

			//Nos posicionamos en el nodo principal del árbol (<rss>)
			Element root = dom.getDocumentElement();

			//Localizamos todos los elementos <item>
			NodeList items = root.getElementsByTagName("item");

			//Recorremos la lista de noticias
			for (int i=0; i<items.getLength(); i++)
			{
				Noticia noticia = new Noticia();

				//Obtenemos la noticia actual
				Node item = items.item(i);

				//Obtenemos la lista de datos de la noticia actual
				NodeList datosNoticia = item.getChildNodes();

				//Procesamos cada dato de la noticia
				for (int j=0; j<datosNoticia.getLength(); j++)
				{
					Node dato = datosNoticia.item(j);
					String etiqueta = dato.getNodeName();

					if (etiqueta.equals("title"))
					{
						String texto = obtenerTexto(dato);
						noticia.setTitulo(texto);
					}
					else if (etiqueta.equals("link"))
					{
						noticia.setLink(dato.getFirstChild().getNodeValue());
					}
					else if (etiqueta.equals("description"))
					{
						String texto = obtenerTexto(dato);
						noticia.setDescripcion(texto);
					}
					else if (etiqueta.equals("guid"))
					{
						noticia.setGuid(dato.getFirstChild().getNodeValue());
					}
					else if (etiqueta.equals("pubDate"))
					{
						noticia.setFecha(dato.getFirstChild().getNodeValue());
					}
				}

				noticias.add(noticia);
			}
		}
		catch (Exception ex)
		{
			throw new RuntimeException(ex);
		}

		return noticias;
	}

	private String obtenerTexto(Node dato)
	{
		StringBuilder texto = new StringBuilder();
		NodeList fragmentos = dato.getChildNodes();

		for (int k=0;k<fragmentos.getLength();k++)
		{
			texto.append(fragmentos.item(k).getNodeValue());
		}

		return texto.toString();
	}

	private InputStream getInputStream()
	{
		try
		{
			return rssUrl.openConnection().getInputStream();
		}
		catch (IOException e)
		{
			throw new RuntimeException(e);
		}
	}
}

Nos centramos una vez más en el método parse(). Al igual que hacíamos para SAX, el primer paso será instanciar una nueva fábrica, esta vez de tipo DocumentBuilderFactory, y posteriormente crear un nuevo parser a partir de ella mediante el método newDocumentBuilder().

Tras esto, ya podemos realizar la lectura del documento XML llamando al métod parse() de nuestro parser DOM, pasándole como parámetro el stream de entrada del fichero. Al hacer esto, el documento XML se leerá completo y se generará la estructura de árbol equivalente, que se devolverá como un objeto de tipo Document. Éste será el objeto que podremos navegar para realizar eltratamiento necesario del XML.

Para ello, lo primero que haremos será acceder al nodo principal del árbol (en nuestro caso, la etiqueta <rss>) utilizando el método getDocumentElement(). Una vez posicionados en dicho nodo, vamos a buscar todos los nodos cuya etiqueta sea <item>. Esto lo conseguimos utilizando el método de búsqueda por nombre de etiqueta, getElementsByTagName(«nombre_de_etiqueta«), que devolverá una lista (de tipo NodeList) con todos los nodos hijos del nodo actual cuya etiqueta coincida con la pasada como parámetro.

Una vez tenemos localizados todos los elementos <item>, que representan a cada noticia, los vamos a recorrer uno a uno para ir generando todos los objetos Noticia necesarios. Para cada uno de ellos, se obtendrán los nodos hijos del elemento mediante getChildNodes() y se recorrerán éstos obteniendo su texto y almacenándolo en el atributo correspondiente del objeto Noticia. Para saber a qué etiqueta corresponde cada nodo hijo utilizamos el método getNodeName().

Merece la pena pararnos un poco en comentar la forma de obtener el texto contenido en un nodo. Como vimos al principio del artículo en el ejemplo gráfico de árbol DOM, el texto de un nodo determinado se almacena a su vez como nodo hijo de dicho nodo. Este nodo de texto suele ser único, por lo que la forma habitual de obtener el texto de un nodo es obtener su primer nodo hijo y de éste último obtener su valor:

String texto = nodo.getFirstChild().getNodeValue();

Sin embargo, en ocasiones, el texto contenido en el nodo viene fragmentado en varios nodos hijos, en vez de sólo uno. Esto ocurre por ejemplo cuando se utilizan en el texto entidades HTML, como por ejemplo &quot; . En estas ocasiones, para obtener el texto completo hay que recorrer todos los nodos hijos e ir concatenando el texto de cada uno para formar el texto completo. Esto es lo que hace nuestra función auxiliar obtenerTexto():

private String obtenerTexto(Node dato)
{
        StringBuilder texto = new StringBuilder();
        NodeList fragmentos = dato.getChildNodes();

        for (int k=0;k<fragmentos.getLength();k++)
        {
            texto.append(fragmentos.item(k).getNodeValue());
        }

        return texto.toString();
}

Como vemos, el modelo DOM nos permite localizar y tratar determinados elementos concretos del documento XML, sin la necesidad de recorrer todo su contenido de principio a fin. Además, a diferencia de SAX, como tenemos cargado en memoria el documento completo de forma persistente (en forma de objeto Document), podremos consultar, recorrer y tratar el documento tantas veces como sea necesario sin necesidad de volverlo a parsear. En un artículo posterior veremos como todas estas características pueden ser ventajas o inconvenientes según el contexto de la aplicación y el tipo de XML tratado.

Puedes consultar y/o descargar el código completo de los ejemplos desarrollados en este artículo accediendo a la página del curso en GitHub.

Curso de Programación Android en PDF

Este curso también está disponible en PDF. Descubre cómo conseguirlo…

¿Te ha sido de utilidad el Curso de Programación Android? ¿Quieres colaborar de forma económica con el proyecto? Puedes contribuir con cualquier cantidad, unos céntimos, unos euros, cualquier aportación será bienvenida. Además, si tu aportación es superior a una pequeña cantidad simbólica recibirás como agradecimiento un documento con la última versión del curso disponible en formato PDF. Sea como sea, muchas gracias por colaborar!

Más información:

También te puede interesar

17 comentarios

Desarrollo en Android | sgoliver.net blog 03/02/2011 - 12:25

[…] Tratamiento de XML en Android (III): DOM […]

Responder
Rrrr 19/02/2011 - 22:57

Hola!

Primero felicitarte por tu blog, me parece muy bueno. Soy un newbie total, sé programar en PHP, pero la orientación a objetos la llevo fatal.

He empezado con el tutorial HelloTabWidget de google. Ahora le quiero poner un lector XML a el programita este, y ahí está esa clase. Ok, entonces, la clase esta dónde se pone? He de crear una clase nueva para ello? O va en el .java principal? O va en los .java que crean las actividades de las pestañas?

Por si no ha quedado claro es una pregunta a muy bajo nivel, muy de base.

Graciassssss de nuevo

Saludos

Responder
OzkaR 23/02/2011 - 19:35

Que tal, Saludos…
La verdad me parece muy buen blog el que tienes…
Solo que yo tengo un dilema… en mi caso en lugar de interpretar un xml, tengo que generarlo a partir de información que genere… entonces seria genial que pudieras hacer algo relacionado a la creación desde cero de estos xml… seria de mucha ayuda…

Gracias…
Saludos

Responder
sgoliver 23/02/2011 - 20:37

Me lo apunto, intentaré escribir un artículo sobre creación de XML.

Responder
Iñaki 03/06/2011 - 19:54

Buenas, solo he visto los artículos relacionados con XML, pero enhorabuena, una aportación magistral…

Gracias

Responder
darkomen 10/08/2011 - 12:56

Buenas, este ejemplo es para ficheros XML que están en una URL específica no?, si tengo el XML en la carpeta /res/raw/fichero.xml, cómo sería para abrirlo?

Responder
admin 10/08/2011 - 13:09

Hola darkomen, puedes ojear los artículos del curso sobre tratamiento de ficheros donde explico cómo acceder a ficheros situados en res/raw. Saludos.

Responder
RocioS 24/10/2011 - 10:04

Buenas, aqui tratas ficheros XML que están en una URL específica, pero como se haría si el fichero esta guardado en la tarjeta SD?
Gracias por tu tutorial, me esta sirviendo de gran ayuda.

Responder
Fernando 29/12/2011 - 21:44

Quisiera saber si puedes publicar como poder mostrar la información obtenida del XML en una interface

Responder
Roger 29/06/2012 - 12:46

Hola, en mi caso el XML que intento parsear tiene atributos en los tags, del estilo

He buscado mucho e intentado opciones como NamedNodeList para conseguir los atributos de una etiqueta, pero no me ha sido posible.

¿Sabes como conseguir los atributos de una etiqueta usando un parser DOM?

Gracias

Responder
Roger 29/06/2012 - 12:49

Vaya, veo que hay código que no me ha dejado poner, en mi caso me estoy refiriendo a casos en los que antes de cerrar el tag con > se le pone un atributo como el típico id=»123″

Responder
Sergio 05/02/2013 - 23:43

En la versión 4.2 de android cuando ejecuta la siguiente instrucción

Document dom = builder.parse(this.getInputStream());

tanto para DOM y SAX, la aplicación se cierra abruptamente y no consigo enterder porque ni como solucionar esto, sin embargo con la version 2.3 esto no pasa y funciona correctamente.

¿Alguna idea de como se puede solucionar este problema?

Gracias.

Responder
Tavo 07/06/2013 - 2:41

Hola

Super bueno tu tutorial, solo tengo un problema, si dejo como en los codigos que pusiste a descargar las ip, jala sin ningun error, pero mi problema viene cuando intento con una dirección ip, siempre truena, tienes idea de por que?

Saludos

Responder
Tavo 07/06/2013 - 15:45

Ya lo pude arreglar, le hice unas actualizaciones y listo. Gracias

Responder
Tratamiento de XML en Android (IV): XmlPull | sgoliver.net blog 21/06/2013 - 10:31

[…] artículos anteriores dedicados al tratamiento de XML en aplicaciones Android (parte 1, parte 2, parte 3) dentro de nuestro tutorial de programación Android hemos comentado ya los modelos SAX y DOM, los […]

Responder
estebusta 17/08/2013 - 5:25

Hola antes q nada muchas gracias por el tutorial, he aprendido mucho, pero tengo una duda… HE TRATADO DE INTEGRAR ESTE TUTORIAL CON EL DE LISTVIEW PARA mostrar el Resultado de mi xml en un List View, pero me rebota un NullPointer Exception.
No se si estoy colocando los metodos en el lugar correcto.. lo que he hecho es:
Colocar en el MainActivity la clase adaptador, gentView y pasarle como Arrayst los datos que recupero del XML, pero no me funciona.
La pregunta es… El .setAdaptor() lo tengo q implementar en el metodo «onclick()» del boton?? o Debo colocarlo en el «onPostExecute»???

Responder
juan carlos 29/04/2019 - 18:07

hola, primero muchas gracias son muy buenos tus blogs me has ayudado mucho pero si me gustaría saber como seria el modelo DOM con un archivo en la carpeta RAW o un archivo creado por mi, ya revise en tus archivos y pero también como guardar el archivo directamente en una carpeta especifica.

Responder

Dejar un comentario

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información. Aceptar Más Información

Política de Privacidad y Cookies