El API JAXP

En la vida real, vamos a tener poca necesidad de mostrar un fichero XML con un analizador SAX. Normalmente queremos procesar los datos de alguna forma para hacer algo �til con ellos. (Si queremos mostrarlo, es m�s f�cil construir un �rbol DOM y usar sus funciones de impresi�n internas. Pero mostrar una estructura XML es una gran forma de ver el analizador SAX en acci�n. En este ejercicio, configuraremos los eventos de "echo" del analizador SAX a System.out.

Consideremos esta versi�n "Hello World" de un programa de proceso XML. Muestra c�mo usar el analizador SAX para obtener el dato y mostrar lo que hemos conseguido.

Nota:

El c�digo explicado en esta secci�n est� en Echo01.java. El fichero opera sobre slideSample01.xml.

.�Crear el Skeleton

Empezamos creando un fichero llamado Echo.java e introducimos el esqueleto para la aplicaci�n:

public class Echo extends HandlerBase
{
    public static void main (String argv[])

    {

    }

}

Esta clase extiende HandlerBase, que implementa todos los interfaces que explicamos en Una Introducci�n a los APIs XML de Java. Que nos permite sobreescribir los m�todos que nos interesan y dejar por defecto el resto.

Como lo vamos a ejecutar en solitario, necesitamos un m�todo main. Y necesitamos argumentos de la l�nea de comandos para que podamos decirle a la aplicaci�n qu� fichero debe mostrar.

.�Importar las Clases que Necesitamos

Luego a�adimos las sentencias import para las clases que usar� la aplicaci�n:

import java.io.*;
import org.xml.sax.*;
import javax.xml.parsers.SAXParserFactory;  
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;

public class Echo extends HandlerBase
{
  ...

Por supuesto, se necesitan las clases de java.io para hacer la salida. El paquete org.xml.sax define todos los interfaces que usaremos para el analizador SAX, la clase SAXParserFactory crea el ejemplar que usaremos. Lazan una ParserConfigurationException si no puede producir un analizador que corresponda con la configuraci�n de opciones especificada. Finalmente, el SAXParser es lo que la factor�a devuelve para analizar.

.�Configurar la I/O

La primera orden de negocio es procesar el argumento de la l�nea de comandos, obtener el nombre del fichero a mostrar, y configurar el canal de salida. A�adimos el texto en negrita de abajo para realizar estas tareas y para hacer una limpieza adicional.

    public static void main (String argv [])

    {
        if (argv.length != 1) {
            System.err.println ("Usage: cmd filename");
            System.exit (1);
        }
        try {
            // Set up output stream
            out = new OutputStreamWriter (System.out, "UTF8");

        } catch (Throwable t) {
            t.printStackTrace ();
        }
        System.exit (0);
    }
    static private Writer out;

Cuando creamos el canal de salida, estamos seleccionando la codificaci�n de caracteres UTF-8. Podr�amos haber elegido US-ASCII, o UTF-16, que tambi�n son soportados por la plataforma Java. Para m�s informaci�n sobre estos conjuntos de caracteres, puedes ver Esquemas de Codificaci�n en Java.

.�Configurar el Analizador

Ahora (por �ltimo) estamos preparados para configurar el analizador. A�adimos el texto en negrita de abajo para configurarlo y arrancarlo.

    public static void main (String argv [])

    {
        if (argv.length != 1) {
            System.err.println ("Usage: cmd filename");
            System.exit (1);
        }

        // Use the default (non-validating) parser
        SAXParserFactory factory = SAXParserFactory.newInstance();
        try {
            // Set up output stream
            out = new OutputStreamWriter (System.out, "UTF8");

            // Parse the input 
            SAXParser saxParser = factory.newSAXParser();
            saxParser.parse( new File(argv [0]), new Echo() );

        } catch (Throwable t) {
            t.printStackTrace ();
        }
        System.exit (0);
    }

Con estas l�neas de c�digo, creamos un ejemplar SAXParserFactory, seg�n lo determina la propiedad del sistema javax.xml.parsers.SAXParserFactory. Entonces obtenemos un analizador de la factor�a y le damos al analizador un ejemplar de esta clase para manejar los eventos, dici�ndole qu� fichero debe procesar.

Nota:

La clase javax.xml.parsers.SAXParser es una envoltura que define un n�mero de m�todos de conveniencia. Envuelve el objeto org.xml.sax.Parser. Si se necesita, podemos obtener el analizador usando el m�todo getParser().

Por ahora, simplemente estamos capturando cualquier excepci�n que el analizador pueda lanzar. Aprenderemos m�s sobre el precesamiento de errores en la secci�n Manejar Errores con el Analizador sin Validaci�n.

Nota:

El m�todo parse que opera sobre objetos File es un m�todo de conveniencia. Debajo de la cubierta, crea un objeto org.xml.sax.InputSource para que el analizador SAX opere sobre �l. Para hacer esto, usa un m�todo est�tico en la clase com.sun.xml.parser.Resolver para crear un InputSource desde un objeto java.io.File. Tambi�n podr�amos hacer esto nosotros mismos, pero el m�todo de conveniencia lo hace m�s sencillo.

.�Implementar el Interface DocumentHandler

El interface m�s importante para nuestro proceso actual es DocumentHandler. Este interface requiere un n�mero de m�todos que invoca el analizador SAX en respuesta a diferentes eventos de an�lixis. Por ahora, s�lo nos conciernen cinco de ellos: startDocument, endDocument, startElement, endElement, y characters. Introducimos el c�digo en negrita de abajo para configurar los m�todos que manejan dichos eventos.

    ...   
    static private Writer	out;
    
    public void startDocument ()
    throws SAXException
    {
    }

    public void endDocument ()
    throws SAXException
    {
    }

    public void startElement (String name, AttributeList attrs)
    throws SAXException
    {
    }

    public void endElement (String name)
    throws SAXException
    {
    }

    public void characters (char buf [], int offset, int len)
    throws SAXException
    {
    }
     ...

Cada uno de estos m�todos debe lanzar una SAXException. Esta excepci�n es devuelta al analizador, qui�n la reenv�a al c�digo que llam� al analizador. En el programa actual, esto significa que vuelve al manejador de la excepci�n Throwable que hay en la parte inferior del m�todo main.

Cuando se encuentra una etiqueta de inicio o de final, el nombre de la etiqueta se pasa como un String a los m�todos startElement o endElement, seg�n corresponda. Cuando se encuentra una etiqueta de inicio, cualquier atributo que defina tambi�n son pasados en un AttributeList. Los caracteres encontrados dentro del elemento se pasa como un array de caracteres, junto con el n�mero de caracteres (longitud) y un desplazamiento dentro del array que apunta al primer caracter.

.�Escribir la Salida

Los m�todos DocumentHandler lanzan SAXExceptions pero no IOExceptions, que pueden ocurrir durante la escritura. La SAXException puede envolver cualquier otra excepci�n, por eso tiene sentido hacer la salida en un m�todo que tiene cuidado de los detalles de manejo de excepciones. A�adimos el c�digo en negrita de abajo para definir un m�todo emit que hace eso.

public void characters (char buf [], int offset, int Len)
throws SAXException
{
}


private void emit (String s)
throws SAXException
{
    try {
        out.write (s);
        out.flush ();
    } catch (IOException e) {
        throw new SAXException ("I/O error", e);
    }
}
...

Cuando se llama a emit, cualquier error de I/O es envuelto en una SAXException junto con un mensaje que lo identifica. Esta excepci�n entonces es lanzada de vuelta al analizador SAX. Por ahora tengamos en mente que emit es un peque�o m�todo que maneja el stream de salida.

.�Espaciar la Salida

Hay un poco m�s de infraestructura que necesitamos hacer antes de realizar cualquier proceso real. A�adimos el c�digo en negrita de abajo para definir el m�todo nl que escribe el tipo de caracter de final de l�nea usado por el sistema actual.

    private void emit (String s)

    ...
        }
  
    private void nl ()
    throws SAXException
    {
        String lineEnd =  System.getProperty("line.separator");
        try {
            out.write (lineEnd);
       
        } catch (IOException e) {
            throw new SAXException ("I/O error", e);
        }
    }
Nota:

Aunque puede parecer un poco aburrido, llamaremos a nl() muchas veces dentro de nuestro c�digo. Definirlo ahora simplificar� el c�digo posterior. Tambi�n proporciona un lugar para indentar la salida cuando a esta secci�n del tutorial.

.�Manejar Eventos de Documento

Finalmente, escribiremos algo de c�digo que realmente procesa los eventos DocumentHandler para los m�todos a�adidos. A�adimos el c�digo en negrita de abajo para manejar los eventos start-document y end-document.

    public void startDocument ()
    throws SAXException
    {
        emit ("<?xml version='1.0' encoding='UTF-8'?>");
        nl();
    }

    public void endDocument ()
    throws SAXException
    {
        try {
            nl();
            out.flush ();
        } catch (IOException e) {
            throw new SAXException ("I/O error", e);
        }
    }

Aqu� hemos mostrado una declaraci�n XML cuando el analizador encuentra el inicio del documento. Como hemos configurado OutputStreamWriter para usar la codificaci�n UTF-8, incluimos �sta especificaci�n como parte de la declaraci�n.

Nota:

Sin embargo, las clases IO no entienden los nombres de codificaciones con guiones, por eso debemos especificar "UTF8" en vez de "UTF-8".

Al final del documento, simplemente poner una nueva l�nea y vaciamos el stream de salida. A�adimos el c�digo en negrita de abajo para procesar los eventos start-element y end-element.

    public void startElement (String name, AttributeList attrs)
    throws SAXException
    {
        emit ("<"+name);
        if (attrs != null) {
            for (int i = 0; i < attrs.getLength (); i++) {             
                emit (" ");
                emit (attrs.getName(i)+"=\""+attrs.getValue (i)+"\"");
            }
        }
        emit (">");
    }

    public void endElement (String name)
    throws SAXException
    {
        emit ("</"+name+">");
    }

Con este c�digo, mostramos las etiquetas de elementos, incluyen cualquier atributo definido en la etiqueta de inicio. Para finalizar esta versi�n del programa, a�adimos el c�digo en negrita de abajo para mostrar los caracteres que ve el analizador.

    
public void characters (char buf [], int offset, int len)
    throws SAXException
    {
        String s = new String(buf, offset, len);
        emit (s);
    }

�Felicidades! Hemos escrito un aplicaci�n analizador SAX. El siguiente paso es compilarlo y ejecutarlo.

Nota:

Para estar seguros, el manejador de caracteres deber�a escanear el buffer para buscar caracteres ampersand ('&') y �ngulos a la izquierda ('<') y reemplazarlos con los strings "&amp;" o "&lt;", seg�n sea apropiado. Encontraremos m�s sobre estos tipos de procesamiento cuando expliquemos las referencias a entidades en Sustituir e Insertar Texto.

.�Compilar el Programa

Para compilar el programa que hemos creado, ejecutaremos el comando apropiado para nuestro sistema (o usaremos los scripts de comandos mencionados abajo).

Windows:

javac -classpath %XML_HOME%\jaxp.jar;%XML_HOME%\parser.jar Echo.java 

Unix:

javac -classpath ${XML_HOME}/jaxp.jar:${XML_HOME}/parser.jar Echo.java

donde:

  • XML_HOME es donde instalamos las librer�as JAXP y Project X.
  • jaxp.jar contiene los APIs especificos JAXP
  • parser.jar contiene los interfaces y clases que componen los APIs SAX y DOM, as� como la implementaci�n de referencia de Sun, Project X.
Nota:

Si estamos usando la versi�n 1.1 de la plataforma tambi�n necesitamos a�adir %JAVA_HOME%\lib\classes.zip tanto para el script de compilaci�n como el de ejecuci�n, donde JAVA_HOME es la localizaci�n de la plataforma Java.

.�Ejecutar el Programa

Para ejecutar el programa, de nuevo ejecutamos los comandos apropiados para nuestro sistema.

Windows:

java -classpath .;%XML_HOME%\jaxp.jar;%XML_HOME%\parser.jar 
Echo slideSample.xml

Unix:

java -classpath .:${XML_HOME}/jaxp.jar:${XML_HOME}/parser.jar 
Echo slideSample.xml 

.�Scripts de Comandos

Para hacernos la vida m�s f�cil, aqu� tenemos algunos scripts de comandos que podemos usar para compilar y ejecutar nuestras aplicaciones mientras trabajemos con este tutorial.

Unix Windows
Scripts constuir, ejecutar build.bat, run.bat
Netscape Pulsa, elige File-->Save As Pulsa con el bot�n derecho, elige
Save Link As
.
Internet
Explorer
-/- Pulsa con el bot�n derecho, elige Save Target As.

.�Chequear la Salida

La salida del programa es almacenada en Echo01-01.log. Aqu� tenemos una parte de �l, mostrando algo de su espaciado.

...
<slideshow title="Sample Slide Show" date="Date of publication" 
author="Yours Truly">


    <slide type="all">
      <title>Wake up to WonderWidgets!</title>
    </slide>
    ...

Mirando esta salida, nos surgen un buen n�mero de preguntas. �De d�nde vienen los espacios verticales extras? y �por qu� estos elementos est�n identados apropiadamente, cuando el c�digo no lo est�? Bien, responderemos a estas preguntas en un momento. Primero hay unos cuantos puntos a observar sobre la salida.

  • El comentario definido en la parte superior del fichero
<!-- A SAMPLE set of slides -->

No aparece en el lista. Los comentarios son ignorados por definici�n, a menos que implementemos un LexicalEventListener en lugar de un DocumentHandler.

  • Los atributos del elemento se listan todos juntos en una s�la linea.

  • La etiqueta del elemento vac�o que definimos en (<item/>) es tratada exactamente igual que un elemento vac�o de dos etiquetas (<item></item>). Para todos los prop�sitos son id�nticos, (s�lo que es m�s f�cil de teclear y consume menos espacio).

.�Identificar los Eventos

Esta versi�n del programa echo podr�a ser �til para mostrar un fichero XML, pero no nos dice mucho sobre hac�a donde va el analizador. El siguiente paso es modificar el programa para que podamos ver de d�nde vienen los espacios y las l�neas verticales.

Nota:

El c�digo descrito en esta secci�n est� en Echo02.java. La salida que produce est� contenida en Echo02-01.log.

Haremos los cambios en negrita que hay abajo para identificar los eventos cuando ocurran.

    public void startDocument ()
    throws SAXException
    {
        nl();
        nl(); 
        emit ("START DOCUMENT");
        nl(); 
        emit ("<?xml version='1.0' encoding='UTF-8'?>");
        nl();
    }

    public void endDocument ()
    throws SAXException
    {
        nl(); emit ("END DOCUMENT");
        try {
         ...
    }

    public void startElement (String name, AttributeList attrs)
    throws SAXException
    {
        nl(); emit ("ELEMENT: ");
        emit ("<"+name);
        if (attrs != null) {
            for (int i = 0; i < attrs.getLength (); i++) {
                emit (" ");
                emit (attrs.getName(i)+"=\""+attrs.getValue (i)+"\"");
                nl(); 
                emit("   ATTR: ");
                emit (attrs.getName (i));
                emit ("\t\"");
                emit (attrs.getValue (i));
                emit ("\"");
            }
        }
        if (attrs.getLength() > 0) nl();
        emit (">");
    }

    public void endElement (String name)
    throws SAXException
    {
        nl(); 
        emit ("END_ELM: ");
        emit ("</"+name+">");
    }

    public void characters (char buf [], int offset, int len)
    throws SAXException
    {   
        nl(); emit ("CHARS: |");     
        String s = new String(buf, offset, len);
        emit (s);
        emit ("|");
    }

Compilamos y ejecutamos esta versi�n del programa para producir una salida m�s informativa. Los atributos se muestran uno por l�nea, que es m�s bonito. Pero, m�s importante, las l�neas de salida se parecen a esta.

CHARS: |



    |

vemos que el m�todo characters es responsable de mostrar tanto los espacios que crean la identaci�n y las m�ltiples nuevas l�neas que separan los atributos.

Nota:

La especificaci�n XML requiere que todos los separadores de l�neas de entrada est�n normalizados a una simple nueva l�nea. El car�cter de nueva l�nea se especifica como \n en Java, C, y en sistemas Unix, pero tiene el alias "linefeed" en sistemas Windows.

.�Comprimir la Salida

Para hacer la salida m�s le�ble, modificamos el programa para que s�lo muestre los caracteres que tienen algo distinto de los espacios en blanco.

Nota:

El c�digo explicado en est� secci�n est� en Echo03.java.

Haremos los cambios mostrados abajo para suprimir de la salida los caracteres que son espacios en blanco.

    public void characters (char buf [], int offset, int len)
    throws SAXException
    {
        nl(); emit ("CHARS: |");
        nl(); emit ("CHARS:   ");
        String s = new String(buf, offset, len);
        emit (s);
        emit ("|");
        if (!s.trim().equals("")) emit (s);
    }

Si ejecutamos el programa ahora, veremos que hemos eliminado tambi�n toda la identaci�n, porque el espacio de identaci�n forma parte de los espacios en blanco que preceden el inicio del elemento. A�adimos el c�digo en negrita de abajo para manejar la identaci�n.

    static private Writer	out;
    
    private String indentString = "    "; // Amount to indent
    private int indentLevel = 0;

    ...
        public void startElement (String name, AttributeList attrs)
    throws SAXException
    {
        indentLevel++;
        nl(); emit ("ELEMENT: ");
        ...
    }

    public void endElement (String name)
    throws SAXException
    {
        nl(); 
        emit ("END_ELM: ");
        emit ("</"+name+">");
        indentLevel--;
    }
    ...
    private void nl ()
    throws SAXException
    {
        ...
        try {
            out.write (lineEnd);
            for (int i=0; i < indentLevel; i++) out.write(indentString);
          
        } catch (IOException e) {
        ...
            }

Este c�digo configura un string de identaci�n, sigue la pista del nivel de identaci�n actual, y saca el string de identaci�n siempre que se llame al m�todo nl.

.�Inspeccionar la Salida

La salida completa para esta versi�n del programa est� contenida en Echo03-01.log.

    ELEMENT: <slideshow
    ...
    CHARS:   
    CHARS:   
        ELEMENT: <slide
        ...  
        END_ELM: </slide>
    CHARS:   
    CHARS:   

Observa que el m�todo characters fue invocado dos veces en una fila. Inspeccionando el fichero fuente slideSample01.xml veremos que hay un comentario antes de la primera diapositiva. La primera llamada a characters viene antes de este comentario. La segunda llamada viene despu�s.

Observa, tambi�n, que el m�todo characters es llamado despu�s del primer elemento slide, as� como antes de �l. Cuando pensamos en t�rminos de una estructura de datos del tipo �rbol, parace obvio. Despu�s de todo, queremos que el elemento slideshow contenga elementos slide, no texto.

En ausencia de un DTD, el analizador debe asumir que cualquier elemento que vea contiene texto como el del primer elemento del slide.

<item>Why <em>WonderWidgets</em> are great</item>

Aqu� , la estructura de �rbol se parece a esto.

ELEMENT: <item>
CHARS:   Why 
    ELEMENT: <em>
    CHARS:   WonderWidgets
    END_ELM: </em>
CHARS:    are great
END_ELM: </item>

.�Documentos y Datos

En este ejemplo, est� claro que hay una mezcla de caracteres con la estructura de los elementos. El hecho de que el texto pueda rodear los elementos nos ayuda a explicar porque algunas veces o�mos hablar sobre "datos XML" y otras veces o�mos hablar sobre "documentos XML". XML maneja confortablemente tanto estructuras de datos como documentos de texto que pueden incluir marcas. La �nica diferencia entre los dos es si se permite o no texto entre los elementos.

Nota:

En una futura secci�n de este tutorial, trabajeremos con el m�todo ignorableWhitespace del interface DocumentHandler. Este m�todo s�lo puede invocarse cuando est� presente un DTD. Si un DTD especifica que slideshow no contiene texto, entonces todos los espacios en blanco que rodean a los elementos slide son ignorables por definici�n. Por otro lado, si slideshow puede contener texto (lo que se debe asumir como verdadero en ausencia de un DTD), el analizador debe asumir que los espacios y l�neas que ve entre los elementos slide son parte importante del documento.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP
ARTÍCULO ANTERIOR

SIGUIENTE ARTÍCULO