Procesamiento de documentos XML en Java usando XPath y XSLT

El lenguaje de marcado extensible (XML) es sin duda una de las tecnologías más novedosas en este momento. Si bien el concepto de lenguajes de marcado no es nuevo, XML parece especialmente atractivo para los programadores de Java e Internet. La API de Java para análisis de XML (JAXP; consulte Recursos), que se ha definido recientemente a través del Proceso de la comunidad de Java, promete proporcionar una interfaz común para acceder a documentos XML. El W3C ha definido el llamado Document Object Model (DOM), que proporciona una interfaz estándar para trabajar con un documento XML en una jerarquía de árbol, mientras que Simple API for XML (SAX) permite que un programa analice un documento XML de forma secuencial, basada en en un modelo de manejo de eventos. Ambos estándares (SAX es un estándar de facto) complementan el JAXP. Juntas, estas tres API brindan soporte suficiente para manejar documentos XML en Java,y numerosos libros en el mercado describen su uso.

Este artículo presenta una forma de manejar documentos XML que va más allá de las API estándar de Java para manipular XML. Veremos que en muchos casos XPath y XSLT proporcionan formas más simples y elegantes de resolver problemas de aplicaciones. En algunas muestras sencillas, compararemos una solución Java / XML pura con una que utiliza XPath y / o XSLT.

Tanto XSLT como XPath son parte de la especificación Extensible Stylesheet Language (XSL) (ver Recursos). XSL consta de tres partes: la propia especificación del lenguaje XSL, las transformaciones XSL (XSLT) y el lenguaje de ruta XML (XPath). XSL es un lenguaje para transformar documentos XML; incluye una definición (Objetos de formato) de cómo se pueden formatear los documentos XML para su presentación. XSLT especifica un vocabulario para transformar un documento XML en otro. Puede considerar que XSLT es XSL menos Objetos de formato. El lenguaje XPath aborda partes específicas de los documentos XML y está diseñado para usarse desde una hoja de estilo XSLT.

Para los propósitos de este artículo, se supone que está familiarizado con los conceptos básicos de XML y XSLT, así como con las API DOM. (Para obtener información y tutoriales sobre estos temas, consulte Recursos).

Nota: Los ejemplos de código de este artículo se compilaron y probaron con el analizador XML Apache Xerces y el procesador Apache Xalan XSL (ver Recursos).

El problema

Muchos artículos y artículos que tratan con XML afirman que es el vehículo perfecto para lograr una buena práctica de diseño en la programación web: el patrón Modelo-Vista-Controlador (MVC) o, en términos más simples, la separación de los datos de la aplicación de los datos de presentación. . Si los datos de la aplicación están formateados en XML, se pueden vincular fácilmente, normalmente en un servlet o en una página de servidor Java, a, por ejemplo, plantillas HTML mediante una hoja de estilo XSL.

Pero XML puede hacer mucho más que simplemente ayudar con la separación de la vista del modelo para la interfaz de una aplicación. Actualmente observamos un uso cada vez más extendido de componentes (por ejemplo, componentes desarrollados con el estándar EJB) que se pueden usar para ensamblar aplicaciones, mejorando así la productividad del desarrollador. La reutilización de componentes se puede mejorar formateando los datos con los que tratan los componentes de forma estándar. De hecho, podemos esperar ver más y más componentes publicados que utilizan XML para describir sus interfaces.

Dado que los datos con formato XML son independientes del idioma, se pueden utilizar en los casos en los que se desconoce el cliente de un servicio de aplicación determinado, o cuando no debe tener ninguna dependencia en el servidor. Por ejemplo, en entornos B2B, puede que no sea aceptable que dos partes tengan dependencias de interfaces de objetos Java concretas para su intercambio de datos. Las nuevas tecnologías como el Protocolo simple de acceso a objetos (SOAP) (ver Recursos) abordan estos requisitos.

Todos estos casos tienen una cosa en común: los datos se almacenan en documentos XML y deben ser manipulados por una aplicación. Por ejemplo, una aplicación que utiliza varios componentes de diferentes proveedores probablemente tendrá que cambiar la estructura de los datos (XML) para adecuarla a las necesidades de la aplicación o adherirse a un estándar determinado.

El código escrito utilizando las API de Java mencionadas anteriormente ciertamente haría esto. Además, cada vez hay más herramientas disponibles con las que puede convertir un documento XML en un JavaBean y viceversa, lo que facilita el manejo de los datos desde un programa Java. Sin embargo, en muchos casos, la aplicación, o al menos una parte de ella, simplemente procesa uno o más documentos XML como entrada y los convierte a un formato XML diferente como salida. El uso de hojas de estilo en esos casos es una alternativa viable, como veremos más adelante en este artículo.

Utilice XPath para localizar nodos en un documento XML

Como se indicó anteriormente, el lenguaje XPath se usa para ubicar ciertas partes de un documento XML. Como tal, está destinado a ser utilizado por una hoja de estilo XSLT, pero nada nos impide usarlo en nuestro programa Java para evitar una iteración prolongada sobre una jerarquía de elementos DOM. De hecho, podemos dejar que el procesador XSLT / XPath haga el trabajo por nosotros. Echemos un vistazo a cómo funciona esto.

Supongamos que tenemos un escenario de aplicación en el que se presenta al usuario un documento XML de origen (posiblemente después de ser procesado por una hoja de estilo). El usuario actualiza los datos y, para ahorrar ancho de banda de la red, envía solo los registros actualizados a la aplicación. La aplicación busca el fragmento XML en el documento fuente que necesita ser actualizado y lo reemplaza con los nuevos datos.

Crearemos una pequeña muestra que le ayudará a comprender las distintas opciones. Para este ejemplo, asumimos que la aplicación trata con registros de direcciones en un addressbook. Un addressbookdocumento de muestra tiene este aspecto:

  John Smith 250 18th Ave SE Rochester MN 55902 Bill Morris 1234 Center Lane NW St. Paul MN 55123   

La aplicación (posiblemente, aunque no necesariamente, un servlet) mantiene una instancia de addressbooken memoria como un Documentobjeto DOM . Cuando el usuario cambia una dirección, la interfaz de la aplicación le envía solo el elemento actualizado .

El elemento se utiliza para identificar de forma única una dirección; sirve como clave principal. Esto no tendría mucho sentido para una aplicación real, pero lo hacemos aquí para simplificar las cosas.

Ahora necesitamos escribir algo de código Java que nos ayude a identificar el elemento en el árbol de fuentes que necesita ser reemplazado por el elemento actualizado. El findAddress()método siguiente muestra cómo se puede lograr. Tenga en cuenta que, para que la muestra sea breve, hemos omitido el manejo de errores apropiado.

Nodo público findAddress (nombre de la cadena, fuente del documento) {Raíz del elemento = fuente.getDocumentElement (); NodeList nl = root.getChildNodes (); // iterar sobre todos los nodos de direcciones y encontrar el que tiene el destinatario correcto para (int i = 0; i
   
    

Es muy probable que el código anterior se pueda optimizar, pero es obvio que iterar sobre el árbol DOM puede ser tedioso y propenso a errores. Ahora veamos cómo se puede ubicar el nodo de destino usando una declaración XPath simple. La declaración podría verse así:

// dirección [hijo :: destinatario [texto () = 'Jim Smith']] 

Ahora podemos reescribir nuestro método anterior. Esta vez, usamos la instrucción XPath para encontrar el nodo deseado:

El nodo público findAddress (nombre de la cadena, origen del documento) arroja Exception {// es necesario recrear algunos objetos auxiliares XMLParserLiaison xpathSupport = new XMLParserLiaisonDefault (); XPathProcessor xpathParser = nuevo XPathProcessorImpl (xpathSupport); PrefixResolver prefixResolver = new PrefixResolverDefault (source.getDocumentElement ()); // crea el XPath e inicialízalo XPath xp = new XPath (); String xpString = "// dirección [hijo :: destinatario [texto () = '" + nombre + "']]"; xpathParser.initXPath (xp, xpString, prefixResolver); // ahora ejecute la instrucción de selección XPath XObject list = xp.execute (xpathSupport, source.getDocumentElement (), prefixResolver); // devuelve la lista de retorno del nodo resultante.nodeset (). item (0); }

Es posible que el código anterior no se vea mucho mejor que el intento anterior, pero la mayor parte del contenido de este método podría encapsularse en una clase auxiliar. La única parte que cambia una y otra vez es la expresión XPath real y el nodo de destino.

Esto nos permite crear una XPathHelperclase, que se ve así:

import org.w3c.dom.*; import org.xml.sax.*; import org.apache.xalan.xpath.*; import org.apache.xalan.xpath.xml.*; public class XPathHelper { XMLParserLiaison xpathSupport = null; XPathProcessor xpathParser = null; PrefixResolver prefixResolver = null; XPathHelper() { xpathSupport = new XMLParserLiaisonDefault(); xpathParser = new XPathProcessorImpl(xpathSupport); } public NodeList processXPath(String xpath, Node target) thrws SAXException { prefixResolver = new PrefixResolverDefault(target); // create the XPath and initialize it XPath xp = new XPath(); xpathParser.initXPath(xp, xpath, prefixResolver); // now execute the XPath select statement XObject list = xp.execute(xpathSupport, target, prefixResolver); // return the resulting node return list.nodeset(); } } 

After creating the helper class, we can rewrite our finder method again, which is now very short:

public Node findAddress(String name, Document source) throws Exception { XPathHelper xpathHelper = new XPathHelper(); NodeList nl = xpathHelper.processXPath( "//address[child::addressee[text() = '"+name+"']]", source.getDocumentElement()); return nl.item(0); } 

The helper class can now be used whenever a node or a set of nodes needs to be located in a given XML document. The actual XPath statement could even be loaded from an external source, so that changes could be made on the fly if the source document structure changes. In this case, no recompile is necessary.

Process XML documents with XSL stylesheets

In some cases, it makes sense to outsource the entire handling of an XML document to an external XSL stylesheet, a process in some respects similar to the use of XPath as described in the previous section. With XSL stylesheets, you can create an output document by selecting nodes from the input document and merging their content with stylesheet content, based on pattern rules.

If an application changes the structure and content of an XML document and producing a new document, it may be better and easier to use a stylesheet to handle the work rather than writing a Java program that does the same job. The stylesheet is most likely stored in an external file, allowing you to change it on the fly, without the need to recompile.

Por ejemplo, podríamos realizar el procesamiento de la addressbookmuestra creando una hoja de estilo que combine la versión en caché de la addressbookcon la actualizada, creando así un nuevo documento con las actualizaciones.

Aquí hay una muestra de dicha hoja de estilo:

   //mymachine.com/changed.xml