Soporte de bibliotecas XML en Java

1. Introducción

En este artículo, compararemos las API y las bibliotecas XML de Java.

Este es el segundo artículo de la serie sobre el soporte de Java para XML; si desea profundizar en el soporte de XPath en Java, consulte el artículo anterior.

2. Resumen

Ahora vamos a profundizar en el soporte del mundo XML y para eso vamos a comenzar explicando lo más simple posible todas las iniciales relacionadas con el tema.

En el soporte XML de Java podemos encontrar pocas definiciones de API, cada una tiene sus pros y sus contras.

SAX : Es una API de análisis basada en eventos, proporciona un acceso de bajo nivel, es eficiente en memoria y más rápida que DOM, ya que no carga todo el árbol de documentos en la memoria, pero no proporciona soporte para la navegación como la proporcionada. por XPath, aunque es más eficiente, también es más difícil de usar.

DOM : Es un analizador basado en modelos que carga un documento de estructura de árbol en la memoria, por lo que tenemos el orden de los elementos originales, podemos navegar nuestro documento en ambas direcciones, proporciona una API para lectura y escritura, ofrece manipulación XML y es muy fácil de usar, aunque el precio supone una gran carga de recursos de memoria.

StAX : Ofrece la facilidad de DOM y la eficiencia de SAX pero carece de algunas funciones proporcionadas por DOM como la manipulación de XML y solo nos permite navegar por el documento hacia adelante.

JAXB : nos permite navegar por el documento en ambas direcciones, es más eficiente que DOM, permite la conversión de tipos XML a java y soporta manipulación XML pero solo puede analizar un documento XML válido.

Todavía puede encontrar algunas referencias a JAXP, pero la última versión de este proyecto es de marzo de 2013 y está prácticamente muerto.

Tabla de API XML

3. El XML

En esta sección vamos a ver las implementaciones más populares, para que podamos probar muestras de trabajo reales y verificar las diferencias entre ellas.

En los siguientes ejemplos, trabajaremos con un archivo XML simple con una estructura como esta:

  Guava Introduction to Guava 04/04/2016 GuavaAuthor  ... 

4. DOM4J

Vamos a empezar echando un vistazo a lo que podemos hacer con DOM4J y para este ejemplo necesitamos agregar la última versión de esta dependencia.

Esta es una de las librerías más populares para trabajar con archivos XML , ya que nos permite realizar lecturas bidireccionales, crear nuevos documentos y actualizar los existentes.

DOM4J puede trabajar con DOM , SAX , XPath y XLST . SAX es compatible a través de JAXP .

Echemos un vistazo aquí, por ejemplo, cómo podemos seleccionar un elemento filtrando por un ID dado.

SAXReader reader = new SAXReader(); Document document = reader.read(file); List elements = document.selectNodes("//*[@tutId='" + id + "']"); return elements.get(0);

La clase SAXReader es responsable de crear un árbol DOM4J a partir de eventos de análisis SAX . Una vez que tenemos un org.dom4j.Document , solo necesitamos llamar al método necesario y pasarle la expresión XPath como una cadena.

Podemos cargar un documento existente, realizar cambios en su contenido y luego actualizar el archivo original.

for (Node node : nodes) { Element element = (Element)node; Iterator iterator = element.elementIterator("title"); while (iterator.hasNext()) { Element title =(Element)iterator.next(); title.setText(title.getText() + " updated"); } } XMLWriter writer = new XMLWriter( new FileWriter(new File("src/test/resources/example_updated.xml"))); writer.write(document); writer.close();

En el ejemplo anterior, estamos cambiando el contenido de cada título y creamos un nuevo archivo.

Observe aquí lo simple que es obtener el nodo de cada título en una lista llamando a elementIterator y pasando el nombre del nodo.

Una vez que tengamos nuestro contenido modificado, usaremos XMLWriter que toma un árbol DOM4J y lo formatea en una secuencia como XML .

Crear un nuevo documento desde cero es tan simple como vemos a continuación.

Document document = DocumentHelper.createDocument(); Element root = document.addElement("XMLTutorials"); Element tutorialElement = root.addElement("tutorial").addAttribute("tutId", "01"); tutorialElement.addAttribute("type", "xml"); tutorialElement.addElement("title").addText("XML with Dom4J"); ... OutputFormat format = OutputFormat.createPrettyPrint(); XMLWriter writer = new XMLWriter( new FileWriter(new File("src/test/resources/example_new.xml")), format); writer.write(document); writer.close(); 

DocumentHelper nos brinda una colección de métodos para que DOM4J los use , como createDocument que crea un documento vacío para comenzar a trabajar con él.

Podemos crear tantos atributos o elementos como necesitemos con los métodos proporcionados por DOM4J , y una vez que tenemos nuestro documento completo, simplemente lo escribimos en un archivo como lo hicimos con el caso de actualización antes.

5. JDOM

Para trabajar con JDOM, tenemos que agregar esta dependencia a nuestro pom.

El estilo de trabajo de JDOM es bastante similar al de DOM4J , por lo que vamos a echar un vistazo a solo un par de ejemplos:

SAXBuilder builder = new SAXBuilder(); Document doc = builder.build(this.getFile()); Element tutorials = doc.getRootElement(); List titles = tutorials.getChildren("tutorial"); 

En el ejemplo anterior, estamos recuperando todos los elementos del elemento raíz de una manera muy simple, como podemos hacer con DOM4J:

SAXBuilder builder = new SAXBuilder(); Document document = (Document) builder.build(file); String filter = "//*[@tutId='" + id + "']"; XPathFactory xFactory = XPathFactory.instance(); XPathExpression expr = xFactory.compile(filter, Filters.element()); List node = expr.evaluate(document);

Nuevamente, aquí en el código anterior, tenemos un SAXBuilder que crea una instancia de documento a partir de un archivo determinado. Estamos recuperando un elemento por su atributo tutId pasando una expresión XPath a XPathFactory proporcionada por JDOM2.

6. STAX

Ahora, veremos cómo podemos recuperar todos los elementos de nuestro elemento raíz usando la API de Stax . Stax está incluido en el JDK desde Java 6, por lo que no es necesario agregar ninguna dependencia.

Primero, necesitamos crear una clase Tutorial :

public class Tutorial { private String tutId; private String type; private String title; private String description; private String date; private String author; // standard getters and setters }

y luego estamos listos para seguir con:

List tutorials = new ArrayList(); XMLInputFactory factory = XMLInputFactory.newInstance(); XMLEventReader eventReader = factory.createXMLEventReader(new FileReader(this.getFile())); Tutorial current; while (eventReader.hasNext()) { XMLEvent event = eventReader.nextEvent(); switch (event.getEventType()) { case XMLStreamConstants.START_ELEMENT: StartElement startElement = event.asStartElement(); String qName = startElement.getName().getLocalPart(); ... break; case XMLStreamConstants.CHARACTERS: Characters characters = event.asCharacters(); ... break; case XMLStreamConstants.END_ELEMENT: EndElement endElement = event.asEndElement(); // check if we found the closing element // close resources that need to be explicitly closed break; } }

En el ejemplo anterior, para ayudarnos a recuperar la información, necesitábamos crear una clase para almacenar los datos recuperados.

Para leer el documento, declaramos lo que se llama controladores de eventos y los usamos para navegar nuestro documento hacia adelante. Recuerde que las implementaciones de SAX no proporcionan navegación bidireccional. Como puede ver aquí, se necesita hacer mucho trabajo solo para recuperar una lista simple de elementos.

7. JAXB

JAXB is included with the JDK, as well as Xerces, se don't need any extra dependency for this one.

It's very simple to load, create and manipulate information from an XML file using JAXB.

We just need to create the correct java entities to bind the XML and that's it.

JAXBContext jaxbContext = JAXBContext.newInstance(Tutorials.class); Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); Tutorials tutorials = (Tutorials) jaxbUnmarshaller.unmarshal(this.getFile());

In the example above, we load our XML file into our object and from there we can handle everything as a normal Java structure;

To create a new document, it is as simple as reading it but doing the reverse way, like done in the below code.

Firstly, we are going to modify our Tutorial class to add JAXB annotations to getters and setters:

public class Tutorial { ... public String getTutId() { return tutId; } @XmlAttribute public void setTutId(String tutId) { this.tutId = tutId; } ... @XmlElement public void setTitle(String title) { this.title = title; } ... } @XmlRootElement public class Tutorials { private List tutorial; // standard getters and setters with @XmlElement annotation }

With @XmlRootElement we define what object is going to represent the root node of our document and then we use @XmlAttribute or @XmlElement to define whether that attribute represents an attribute of a node or an element of the document.

Then we can follow with:

Tutorials tutorials = new Tutorials(); tutorials.setTutorial(new ArrayList()); Tutorial tut = new Tutorial(); tut.setTutId("01"); ... tutorials.getTutorial().add(tut); JAXBContext jaxbContext = JAXBContext.newInstance(Tutorials.class); Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); jaxbMarshaller.marshal(tutorials, file);

As you can see, binding XML file to Java objects is the easiest way to work this kind of files.

8. XPath Expression Support

To create complex XPath expressions, we can use Jaxen. This is an open source XPath library adaptable to many different object models, including DOM, XOM, DOM4J, and JDOM.

We can create XPath expressions and compile them against many supported documents.

String expression = "/tutorials/tutorial"; XPath path = new DOMXPath(expression); List result = path.selectNodes(xmlDocument);

To make it work we'll need to add this dependency to our project.

9. Conclusion

As you can see there are many options for working with XML, depending on the requirements of your application, you could work with any of them or you may have to choose between efficiency and simplicity.

You can find the full working samples for this article in our git repository here.