Programación de XML en Java, Parte 1

Entonces, comprende (más o menos) cómo representaría sus datos en XML y está interesado en usar XML para resolver muchos de sus problemas de administración de datos. Sin embargo, no está seguro de cómo utilizar XML con sus programas Java.

TEXTBOX: TEXTBOX_HEAD: Programación XML en Java: ¡Lea toda la serie!

  • Parte 1. Utilice la API simple para XML (SAX) para procesar XML en Java fácilmente
  • Parte 2. Aprenda sobre la validación de SAX y XML a través de ejemplos ilustrativos
  • Parte 3. DOMINACIÓN: Toma el control de documentos estructurados con el Modelo de objetos de documento

: END_TEXTBOX

Este artículo es una continuación de mi artículo introductorio, "XML para principiantes absolutos", en la edición de abril de 1999 de JavaWorld (consulte la sección Recursos a continuación para obtener la URL). Ese artículo describía XML; Ahora me basaré en esa descripción y mostraré en detalle cómo crear una aplicación que utilice la API simple para Java (SAX), una API estándar de Java ligera y potente para procesar XML.

El código de ejemplo utilizado aquí utiliza la API SAX para leer un archivo XML y crear una estructura útil de objetos. Cuando haya terminado este artículo, estará listo para crear sus propias aplicaciones basadas en XML.

La virtud de la pereza

Larry Wall, el genio loco creador de Perl (el segundo lenguaje de programación más grande que existe), ha declarado que la pereza es una de las "tres grandes virtudes" de un programador (las otras dos son la impaciencia y la arrogancia). La pereza es una virtud porque un programador perezoso hará todo lo posible para evitar el trabajo, incluso yendo tan lejos como para crear marcos de programación generales y reutilizables que se pueden usar repetidamente. La creación de estos marcos implica mucho trabajo, pero el tiempo que se ahorra en futuras asignaciones compensa con creces el esfuerzo inicial invertido. Los mejores marcos permiten a los programadores hacer cosas increíbles con poco o ningún trabajo, y es por eso que la pereza es virtuosa.

XML es una tecnología habilitadora para el programador virtuoso (vago). Un analizador XML básico hace mucho trabajo para el programador, reconociendo tokens, traduciendo caracteres codificados, aplicando reglas en la estructura de archivos XML, verificando la validez de algunos valores de datos y realizando llamadas al código específico de la aplicación, cuando sea apropiado. De hecho, la estandarización temprana, combinada con un mercado ferozmente competitivo, ha producido decenas de implementaciones disponibles gratuitamente de analizadores XML estándar en muchos lenguajes, incluidos C, C ++, Tcl, Perl, Python y, por supuesto, Java.

La API SAX es una de las interfaces más simples y ligeras para manejar XML. En este artículo, usaré la implementación XML4J de IBM de SAX, pero dado que la API está estandarizada, su aplicación podría sustituir a cualquier paquete que implemente SAX.

SAX es una API basada en eventos que opera según el principio de devolución de llamada. Un programador de aplicaciones normalmente creará un Parserobjeto SAX y le pasará tanto el XML de entrada como un controlador de documentos, que recibe devoluciones de llamada para eventos SAX. El SAX Parserconvierte su entrada en un flujo de eventos correspondientes a las características estructurales de la entrada, como etiquetas XML o bloques de texto. A medida que ocurre cada evento, se pasa al método apropiado de un manejador de documentos definido por el programador, que implementa la interfaz de devolución de llamada org.xml.sax.DocumentHandler. Los métodos de esta clase de controlador realizan la funcionalidad específica de la aplicación durante el análisis.

Por ejemplo, imagine que un analizador SAX recibe un documento que contiene el pequeño documento XML que se muestra en el Listado 1 a continuación. (Consulte Recursos para obtener el archivo XML).

 Ogden Nash Fleas Adam los tenía.  

Listado 1. XML que representa un poema corto

Cuando el analizador SAX encuentra la etiqueta, llama al definido por el usuario DocumentHandler.startElement()con la cadena POEMcomo argumento. Implementas el startElement()método para hacer lo que la aplicación debe hacer cuando POEMcomienza. El flujo de eventos y las llamadas resultantes para la pieza de XML anterior aparece en la Tabla 1 a continuación.

Tabla 1. La secuencia de devoluciones de llamada que produce SAX al analizar el Listado 1
Elemento encontrado Devolución de llamada del analizador
{Principio del documento} startDocument()
startElement("POEM", {AttributeList})
"\norte" characters("\n...", 6, 1)
startElement("AUTHOR", {AttributeList})
"Ogden Nash" characters("\n...", 15, 10)
endElement("AUTHOR")
"\norte" characters("\n...", 34, 1)
startElement("TITLE", {AttributeList})
"Pulgas" characters("\n...", 42, 5)
endElement("TITLE")
"\norte" characters("\n...", 55, 1)
startElement("LINE", {AttributeList})
"Adán" characters("\n...", 62, 4)
endElement("LINE")
startElement("LINE", {AttributeList})
"Los tenía". characters("\n...", 67, 8)
endElement("LINE")
"\norte" characters("\n...", 82, 1)
endElement("POEM")
{Fin del documento} endDocument()

Creas una clase que se implementa DocumentHandlerpara responder a eventos que ocurren en el analizador SAX. Estos eventos no son eventos de Java, como puede que los conozca del Abstract Windowing Toolkit (AWT). Son condiciones que el analizador SAX detecta mientras analiza, como el inicio de un documento o la aparición de una etiqueta de cierre en el flujo de entrada. A medida que ocurre cada una de estas condiciones (o eventos), SAX llama al método correspondiente a la condición en su DocumentHandler.

Por lo tanto, la clave para escribir programas que procesen XML con SAX es averiguar qué DocumentHandlerdebe hacer en respuesta a una serie de devoluciones de llamadas de métodos de SAX. El analizador SAX se encarga de todos los mecanismos de identificación de etiquetas, sustitución de valores de entidad, etc., dejándolo libre para concentrarse en la funcionalidad específica de la aplicación que utiliza los datos codificados en XML.

La Tabla 1 muestra solo eventos asociados con elementos y personajes. SAX también incluye funciones para manejar otras características estructurales de archivos XML, como entidades e instrucciones de procesamiento, pero estas están más allá del alcance de este artículo.

El lector astuto se dará cuenta de que un documento XML se puede representar como un árbol de objetos escritos, y que el orden del flujo de eventos presentados al DocumentHandlercorresponde a un recorrido en orden, en profundidad, primero del árbol del documento. (No es esencial comprender este punto, pero el concepto de un documento XML como una estructura de datos de árbol es útil en tipos más sofisticados de procesamiento de documentos, que se tratarán en artículos posteriores de esta serie).

La clave para comprender cómo usar SAX es comprender la DocumentHandlerinterfaz, que discutiré a continuación.

Personalice el analizador con org.xml.sax.DocumentHandler

Since the DocumentHandler interface is so central to processing XML with SAX, it's worthwhile to understand what the methods in the interface do. I'll cover the essential methods in this section, and skip those that deal with more advanced topics. Remember, DocumentHandler is an interface, so the methods I'm describing are methods that you will implement to handle application-specific functionality whenever the corresponding event occurs.

Document initialization and cleanup

For each document parsed, the SAX XML parser calls the DocumentHandler interface methods startDocument() (called before processing begins) and endDocument() (called after processing is complete). You can use these methods to initialize your DocumentHandler to prepare it for receiving events and to clean up or produce output after parsing is complete. endDocument() is particularly interesting, since it's only called if an input document has been successfully parsed. If the Parser generates a fatal error, it simply aborts the event stream and stops parsing, and endDocument() is never called.

Processing tags

The SAX parser calls startElement() whenever it encounters an open tag, and endElement() whenever it encounters a close tag. These methods often contain the code that does the majority of the work while parsing an XML file. startElement()'s first argument is a string, which is the tag name of the element encountered. The second argument is an object of type AttributeList, an interface defined in package org.xml.sax that provides sequential or random access to element attributes by name. (You've undoubtedly seen attributes before in HTML; in the line

BORDER

Since SAX doesn't provide any information about the context of the elements it encounters (that appears inside in Listing 1 above, for example), it is up to you to supply that information. Application programmers often use stacks in startElement() and endElement(), pushing objects onto a stack when an element starts, and popping them off of the stack when the element ends.

Process blocks of text

The characters() method indicates character content in the XML document -- characters that don't appear inside an XML tag, in other words. This method's signature is a bit odd. The first argument is an array of bytes, the second is an index into that array indicating the first character of the range to be processed, and the third argument is the length of the character range.

It might seem that an easier API would have simply passed a String object containing the data, but characters() was defined in this way for efficiency reasons. The parser has no way of knowing whether or not you're going to use the characters, so as the parser parses its input buffer, it passes a reference to the buffer and the indices of the string it is viewing, trusting that you will construct your own String if you want one. It's a bit more work, but it lets you decide whether or not to incur the overhead of String construction for content pieces in an XML file.

The characters() method handles both regular text content and content inside CDATA sections, which are used to prevent blocks of literal text from being parsed by an XML parser.

Other methods

Hay otros tres métodos en la DocumentHandlerinterfaz: ignorableWhitespace(), processingInstruction(), y setDocumentLocator(). ignorableWhitespace()informa sobre apariciones de espacios en blanco y normalmente no se utiliza en analizadores SAX no validantes (como el que estamos usando para este artículo); processingInstruction()maneja la mayoría de las cosas dentro and ?> delimiters; and setDocumentLocator() is optionally implemented by SAX parsers to give you access to the locations of SAX events in the original input stream. You can read up on these methods by following the links on the SAX interfaces in Resources.

Implementing all of the methods in an interface can be tedious if you're only interested in the behavior of one or two of them. The SAX package includes a class called HandlerBase that basically does nothing, but can help you take advantage of just one or two of these methods. Let's examine this class in more detail.

HandlerBase: A do-nothing class

Often, you're only interested in implementing one or two methods in an interface, and want the other methods to simply do nothing. The class org.xml.sax.HandlerBase simplifies the implementation of the DocumentHandler interface by implementing all of the interface's methods with do-nothing bodies. Then, instead of implementing DocumentHandler, you can subclass HandlerBase, and only override the methods that interest you.

For example, say you wanted to write a program that just printed the title of any XML-formatted poem (like TitleFinder in Listing 1). You could define a new DocumentHandler, like the one in Listing 2 below, that subclasses HandlerBase, and only overrides the methods you need. (See Resources for an HTML file of TitleFinder.)

012 /** 013 * SAX DocumentHandler class that prints the contents of "TITLE" element 014 * of an input document. 015 */ 016 public class TitleFinder extends HandlerBase { 017 boolean _isTitle = false; 018 public TitleFinder() { 019 super(); 020 } 021 /** 022 * Print any text found inside a  element. 023 */ 024 public void characters(char[] chars, int iStart, int iLen) { 025 if (_isTitle) { 026 String sTitle = new String(chars, iStart, iLen); 027 System.out.println("Title: " + sTitle); 028 } 029 } 030 /** 031 * Mark title element end. 032 */ 033 public void endElement(String element) { 034 if (element.equals("TITLE")) { 035 _isTitle = false; 036 } 037 } 038 /** 039 * Find contents of titles 040 */ 041 public static void main(String args[]) { 042 TitleFinder titleFinder = new TitleFinder(); 043 try { 044 Parser parser = ParserFactory.makeParser("com.ibm.xml.parsers.SAXParser"); 045 parser.setDocumentHandler(titleFinder); 046 parser.parse(new InputSource(args[0])); 047 } catch (Exception ex) { 048 ; // OK, so sometimes laziness *isn't* a virtue. 049 } 050 } 051 /** 052 * Mark title element start 053 */ 054 public void startElement(String element, AttributeList attrlist) { 055 if (element.equals("TITLE")) { 056 _isTitle = true; 057 } 058 } 

Listing 2. TitleFinder: A DocumentHandler derived from HandlerBase that prints TITLEs


#####


, es un atributo cuyo valor es "1"). Dado que el Listado 1 no incluye atributos, no aparecen en la Tabla 1. Verá ejemplos de atributos en la aplicación de muestra más adelante en este artículo.