StAX (Xml Pull Parser): Streaming API para XML

2
15003

StAX (Xml Pull Parser): Streaming API para XML

Introducción.

Desde hace años los documentos XML han dominado como formato de intercambio de información, creándose a su alrededor infinidad de tecnologías y lenguajes.

En la actualidad las dos formas más extendidas de leer documentos XML son DOM y SAX, ambas son especificaciones con numerosas implementaciones y sobre las que se apoyan otras tecnologías como XSLT, XPath, etc..

Ahora bien, estas dos tecnologías no siempre son apropiadas para todos los problemas. Por ejemplo, imagine que tenemos un XML de muchos megabytes con el siguiente formato y
queremos saber cuál de entre los 100 primeros libros es el más caro:


      
          
              Libro 1
              drama
              35
              400
          
          
          
          
          
          
          
              Libro 10000000
              Ciencia
              55
              200
                              
      
    

Pues bien, para este tipo de tareas ni DOM ni SAX son adecuadas, pues:

  • Con SAX tendríamos que parsear todo el archivo.
  • Con DOM tendríamos que tener previamente toda la información en memoria para recorrerla » OutOfMemoryException

Si te pones a pensar, te das cuenta de que ninguna de las dos tecnologías es apropiada por que el programador pierde el control del proceso de parseo.

En este tipo de problemas encajan los parsers XML conocidos como Pull Parsers en donde el programador toma el control del proceso de parseo.

StAX

Algunas frases sobre StAX:

  1. StAX son las siglas de Streaming API for XML.
  2. Es una especificación cuyas clases e interfaces están ubicadas en el paquete javax.xml.stream.
  3. Java 6 incluye una implementación de referencia. [Ver implementaciones]
  4. Streaming API for XML consiste en dos estilos de parseo (ambos sencillisimos de usar): Cursor API y Event Iterator API.
Excelente tabla comparativa de características: [Fuente]

Familias de parsers: [Fuente]

Estilo Cursor API

Para hacer uso de este estilo usaremos el interface XMLStreamReader.

Es la forma más eficiente de leer el XML pues nos movernos a bajo nivel.

Con este estilo el XML se recorre iterando ha través de los métodos:

  • public boolean hasNext():

       Devuelve un booleano indicando si hay más elementos que iterar.
  • public int next():

       Devuelve el código del evento en el que estamos posicionados y cuyos valores están definidos como constantes en la interfaz javax.xml.stream.XMLStreamConstants.

    • XMLStreamConstants.START_DOCUMENT: Principio del documento.
    • XMLStreamConstants.START_ELEMENT: Principio de un elemento o tag.
    • XMLStreamConstants.END_ELEMENT: Fin de un elemento o tag.
    • XMLStreamConstants.START_ELEMENT: Fin del documento.
    • etc. (consultar documentación oficial)
  • public String getLocalName(): Para al nombre del tag cuando el evento es: CHARACTERS
  • public String getElementText(): Para acceder a los datos cuando el tipo de evento es alguno de los siguientes: START_ELEMENT, END_ELEMENT o ENTITY_REFERENCE.
  • etc. (consultar documentación oficial)
Resolución con Cursor API: ¿Cuál de entre los 100 primeros libros es el más caro?:
package com.autentia.tutoriales.axiom;

import javax.xml.stream.*;
import java.io.*;

/**
 * Ejemplo de lectura de datos con StAX a través del método: cursor API
 * @author Carlos García. Autentia.
 */
public class StaxXMLStreamReaderApp {

	/**
	 * Imprime por la salida estandar el título del libro más caro de entre los 100 primeros libros
	 */
	public static void main(String[] args) {
		FileReader		reader = null;
		XMLStreamReader parser = null;
		int		precioMasCaro  = Integer.MIN_VALUE;
		String	tituloMasCaro  = null;
		int 	currentPrecio  = 0; 
		String	currentTitle   = null;
		int 	currentPositon = 0;
		int	    eventCode;
		String  tag;
		
		try {
			reader = new FileReader("c:/xml_muy_grande.xml");
			parser = XMLInputFactory.newInstance().createXMLStreamReader(reader);
			while (parser.hasNext() && (currentPositon != 100)){
				eventCode = parser.next();
				if (eventCode == XMLStreamConstants.START_ELEMENT){
					tag = parser.getLocalName();
					if ("libro".equals(tag)){
						currentPositon++;
					} else if ("titulo".equals(tag)){
						currentTitle = parser.getElementText();
					} else if ("precio".equals(tag)){
						currentPrecio = Integer.valueOf(parser.getElementText());
						if (currentPrecio > precioMasCaro){
							precioMasCaro = currentPrecio;
							tituloMasCaro = currentTitle;
						}
					}
				}
			}
			
			System.out.println("El libro más caro es: " + tituloMasCaro);
		} catch (Exception ex) {
			// Para este tutorial no hacemos nada más que imprimir la excepción
			System.out.println(ex);
		} finally {
			try {
				reader.close();
			} catch (Exception ex){}			
			try {
				parser.close();
			} catch (Exception ex){}    	
		}		
	}
}
    

Estilo Event Iterator API

En este estilo de parseo se crean objetos intermedios que pueden ser pasados como parámetros a otros métodos de tu lógica de negocio.
Se encuentra ubicado como una capa por encima del estilo cursor API.

Los métodos son muy parecidos al Cursor API, consulte la documentación para profundizar más al respecto.

Resolución con Event Iterator API: ¿Cuál de entre los 100 primeros libros es el más caro?:
package com.autentia.tutoriales.axiom;

import javax.xml.stream.*;
import javax.xml.stream.events.XMLEvent;
import java.io.*;

/**
 * Ejemplo de lectura de datos con StAX a través del método: Event Iterator API
 * @author Carlos García. Autentia.
 */
public class StaxXMLEventReadeApp {

	/**
	 * Imprime por la salida estandar el título del libro más caro de entre los 100 primeros libros
	 */
	public static void main(String[] args) {
		FileReader		reader = null;
		XMLEventReader parser = null;
		int		precioMasCaro  = Integer.MIN_VALUE;
		String	tituloMasCaro  = null;
		int 	currentPrecio  = 0; 
		String	currentTitle   = null;
		int 	currentPositon = 0;
		XMLEvent evt   = null;
		String   tag  = null; 
		
		try {
			reader = new FileReader("c:/xml_muy_grande.xml");
			parser  = XMLInputFactory.newInstance().createXMLEventReader(reader);
			while (parser.hasNext() && (currentPositon != 100)){
				evt = parser.nextEvent();
				
				if (evt.isStartElement()){
					tag = evt.asStartElement().getName().getLocalPart();
					
					if ("libro".equals(tag)){
						currentPositon++;
					} else if ("titulo".equals(tag)){
						currentTitle = parser.getElementText();
					} else if ("precio".equals(tag)){
						currentPrecio = Integer.valueOf(parser.getElementText());
						if (currentPrecio > precioMasCaro){
							precioMasCaro = currentPrecio;
							tituloMasCaro = currentTitle;
						}
					}						
				}
			}
			
			System.out.println("El libro más caro es: " + tituloMasCaro);
		} catch (Exception ex) {
			// Para este tutorial no hacemos nada más que imprimir la excepción
			System.out.println(ex);
		} finally {
			try {
				reader.close();
			} catch (Exception ex){}			
			try {
				parser.close();
			} catch (Exception ex){}    	
		}		
	}
}

Referencias

Conclusiones

En muchas ocasiones la falta de formación, de tiempo, o de ganas, hacen que los proyectos usen parsers que no son los más adecuados para el problema a resolver y esto hace que los sistemas consuman muchos más recursos de los necesarios, generandose problemas del tipo OutOfMemoryException…

La tecnología está, ahora sólo hace falta conocerla y saber cuando usarla.

Como cuña para terminar, decir que este tipo de parsers (Pull Parser) se suelen usar en aplicaciones para dispositivos de recursos limitados (PDA, móviles, etc). En este tipo de proyectos cada byte consumido importa.

Carlos García Pérez. Creador de MobileTest, un complemento educativo para los profesores y sus alumnos.

cgpcosmad@gmail.com

2 COMENTARIOS

  1. Excelente articulo!.
    Un proceso que demoraba una hora con XPath, a parte de consumir mucha memoria, con StAX demora ahora 4 minutos. He quedado sorprendida.

    Muchas gracias por el aporte!!!!!!!

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad