Java Language
Análisis XML utilizando las API de JAXP
Buscar..
Observaciones
El análisis XML es la interpretación de documentos XML para manipular su contenido mediante construcciones sensibles, ya sean "nodos", "atributos", "documentos", "espacios de nombres" o eventos relacionados con estas construcciones.
Java tiene una API nativa para el manejo de documentos XML, llamada JAXP, o API de Java para el procesamiento XML . JAXP y una implementación de referencia se han incluido en todas las versiones de Java desde Java 1.4 (JAXP v1.1) y han evolucionado desde entonces. Java 8 enviado con la versión 1.6 de JAXP.
La API proporciona diferentes formas de interactuar con documentos XML, que son:
- La interfaz DOM (Modelo de Objeto de Documento)
- La interfaz SAX (API simple para XML)
- La interfaz StAX (Streaming API para XML)
Principios de la interfaz DOM
La interfaz DOM apunta a proporcionar una forma compatible con W3C DOM de interpretar XML. Varias versiones de JAXP han admitido varios niveles de especificación de DOM (hasta el nivel 3).
Bajo la interfaz del Modelo de objetos de documento, un documento XML se representa como un árbol, comenzando con el "Elemento del documento". El tipo base de la API es el tipo de Node
, permite navegar de un Node
a su padre, sus hijos o sus hermanos (aunque, no todos los Node
pueden tener hijos, por ejemplo, los nodos de Text
son finales en el árbol, y nunca tener hijos). Las etiquetas XML se representan como Element
s, que amplían notablemente el Node
con métodos relacionados con atributos.
La interfaz DOM es muy útil ya que permite un análisis de una línea de documentos XML como árboles y permite una modificación fácil del árbol construido (adición de nodo, supresión, copia, ...) y, finalmente, su serialización (de nuevo al disco). ) modificaciones posteriores. Sin embargo, esto tiene un precio: el árbol reside en la memoria, por lo tanto, los árboles DOM no siempre son prácticos para grandes documentos XML. Además, la construcción del árbol no siempre es la forma más rápida de tratar con el contenido XML, especialmente si uno no está interesado en todas las partes del documento XML.
Principios de la interfaz SAX
La API de SAX es una API orientada a eventos para tratar con documentos XML. Bajo este modelo, los componentes de un documento XML se interpretan como eventos (por ejemplo, "se ha abierto una etiqueta", "se ha cerrado una etiqueta", "se ha encontrado un nodo de texto", "se ha encontrado un comentario"). ..
La API de SAX utiliza un enfoque de "análisis de inserción", donde un Parser
SAX es responsable de interpretar el documento XML e invoca métodos en un delegado (un ContentHandler
) para tratar cualquier evento que se encuentre en el documento XML. Por lo general, uno nunca escribe un analizador, pero proporciona un controlador para recopilar todas las informaciones necesarias del documento XML.
La interfaz SAX supera las limitaciones de la interfaz DOM manteniendo solo los datos mínimos necesarios en el nivel del analizador (por ejemplo, contextos de espacios de nombres, estado de validación), por lo tanto, solo las informaciones que guarda ContentHandler
, de las que usted, el desarrollador, es responsable, son guardado en la memoria. La desventaja es que no hay manera de "retroceder en el tiempo / el documento XML" con este enfoque: mientras que DOM permite que un Node
regrese a su padre, no existe tal posibilidad en SAX.
Principios de la interfaz StAX
La API StAX adopta un enfoque similar para procesar XML como la API SAX (es decir, impulsada por eventos), la única diferencia muy significativa es que StAX es un analizador de extracción (donde SAX era un analizador de inserción). En SAX, el Parser
está en control y utiliza devoluciones de llamada en el ContentHandler
. En Stax, usted llama al analizador y controla cuándo / si desea obtener el siguiente "evento" XML.
La API comienza con XMLStreamReader (o XMLEventReader ), que son las puertas de acceso a través de las cuales el desarrollador puede preguntar a nextEvent()
, de forma nextEvent()
.
Analizar y navegar un documento utilizando la API DOM
Teniendo en cuenta el siguiente documento:
<?xml version='1.0' encoding='UTF-8' ?>
<library>
<book id='1'>Effective Java</book>
<book id='2'>Java Concurrency In Practice</book>
</library>
Uno puede usar el siguiente código para construir un árbol DOM a partir de una String
:
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;
public class DOMDemo {
public static void main(String[] args) throws Exception {
String xmlDocument = "<?xml version='1.0' encoding='UTF-8' ?>"
+ "<library>"
+ "<book id='1'>Effective Java</book>"
+ "<book id='2'>Java Concurrency In Practice</book>"
+ "</library>";
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
// This is useless here, because the XML does not have namespaces, but this option is usefull to know in cas
documentBuilderFactory.setNamespaceAware(true);
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
// There are various options here, to read from an InputStream, from a file, ...
Document document = documentBuilder.parse(new InputSource(new StringReader(xmlDocument)));
// Root of the document
System.out.println("Root of the XML Document: " + document.getDocumentElement().getLocalName());
// Iterate the contents
NodeList firstLevelChildren = document.getDocumentElement().getChildNodes();
for (int i = 0; i < firstLevelChildren.getLength(); i++) {
Node item = firstLevelChildren.item(i);
System.out.println("First level child found, XML tag name is: " + item.getLocalName());
System.out.println("\tid attribute of this tag is : " + item.getAttributes().getNamedItem("id").getTextContent());
}
// Another way would have been
NodeList allBooks = document.getDocumentElement().getElementsByTagName("book");
}
}
El código produce lo siguiente:
Root of the XML Document: library
First level child found, XML tag name is: book
id attribute of this tag is : 1
First level child found, XML tag name is: book
id attribute of this tag is : 2
Analizar un documento utilizando la API StAX
Teniendo en cuenta el siguiente documento:
<?xml version='1.0' encoding='UTF-8' ?>
<library>
<book id='1'>Effective Java</book>
<book id='2'>Java Concurrency In Practice</book>
<notABook id='3'>This is not a book element</notABook>
</library>
Uno puede usar el siguiente código para analizarlo y construir un mapa de títulos de libros por ID de libro.
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamReader;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;
public class StaxDemo {
public static void main(String[] args) throws Exception {
String xmlDocument = "<?xml version='1.0' encoding='UTF-8' ?>"
+ "<library>"
+ "<book id='1'>Effective Java</book>"
+ "<book id='2'>Java Concurrency In Practice</book>"
+ "<notABook id='3'>This is not a book element </notABook>"
+ "</library>";
XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory();
// Various flavors are possible, e.g. from an InputStream, a Source, ...
XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(new StringReader(xmlDocument));
Map<Integer, String> bookTitlesById = new HashMap<>();
// We go through each event using a loop
while (xmlStreamReader.hasNext()) {
switch (xmlStreamReader.getEventType()) {
case XMLStreamConstants.START_ELEMENT:
System.out.println("Found start of element: " + xmlStreamReader.getLocalName());
// Check if we are at the start of a <book> element
if ("book".equals(xmlStreamReader.getLocalName())) {
int bookId = Integer.parseInt(xmlStreamReader.getAttributeValue("", "id"));
String bookTitle = xmlStreamReader.getElementText();
bookTitlesById.put(bookId, bookTitle);
}
break;
// A bunch of other things are possible : comments, processing instructions, Whitespace...
default:
break;
}
xmlStreamReader.next();
}
System.out.println(bookTitlesById);
}
Esto produce:
Found start of element: library
Found start of element: book
Found start of element: book
Found start of element: notABook
{1=Effective Java, 2=Java Concurrency In Practice}
En esta muestra, uno debe tener cuidado con algunas cosas:
El uso de
xmlStreamReader.getAttributeValue
funciona porque hemos verificado primero que el analizador está en el estadoSTART_ELEMENT
. En todos los demás estados (exceptoATTRIBUTES
), el analizador está obligado a lanzar laIllegalStateException
, porque los atributos solo pueden aparecer al principio de los elementos.Lo mismo ocurre con
xmlStreamReader.getTextContent()
, funciona porque estamos en unSTART_ELEMENT
y sabemos en este documento que el elemento<book>
no tiene nodos secundarios que no sean de texto.
Para el análisis de documentos más complejos (elementos más profundos, anidados, ...), es una buena práctica "delegar" el analizador a sub-métodos u otros objetos, por ejemplo, tener una clase o método BookParser
, y hacer que se ocupe de cada elemento de START_ELEMENT a END_ELEMENT de la etiqueta XML del libro.
También se puede usar un objeto de Stack
para mantener datos importantes arriba y abajo del árbol.