Descripción general
Los documentos XML son documentos jerárquicos, en los que los mismos nombres de elementos y espacios de nombres pueden aparecer en varios lugares, con diferentes significados y con una profundidad infinitiva (recursiva). Como es normal, la solución a los grandes problemas es dividirlos en pequeños problemas. En el contexto del análisis de XML, esto significa analizar partes específicas de XML en métodos específicos de ese XML. Por ejemplo, una parte de la lógica analizaría una dirección:
<Address>
<Street>Odins vei</Street>
<Building>4</Building>
<Door>b</Door>
</Address>
es decir, tendrías un método
AddressType parseAddress(...);
o
void parseAddress(...);
en algún lugar de su lógica, tomando argumentos de entrada XML y devolviendo un objeto (el resultado de B se puede obtener de un campo más adelante).
SAX
SAX 'empuja' los eventos XML , dejando que usted determine dónde pertenecen los eventos XML en su programa / datos.
// method in stock SAX handler
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException
// .. your logic here for start element
}
En el caso de un elemento de inicio 'Construcción', necesitaría determinar que en realidad está analizando una Dirección y luego enrutar el evento XML al método cuyo trabajo es interpretar la Dirección.
StAX
StAX 'extrae' eventos XML , dejando que usted determine en qué parte de su programa / datos recibir los eventos XML.
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
}
Por supuesto, siempre querrá recibir un evento de 'Construcción' en el método cuyo trabajo es interpretar la Dirección.
Discusión
La diferencia entre SAX y StAX es la de empujar y tirar. En ambos casos, el estado de análisis debe manejarse de alguna manera.
Esto se traduce en el método B como típico para SAX y el método A para StAX. Además, SAX debe proporcionar B eventos XML individuales, mientras que StAX puede proporcionar A varios eventos (pasando una instancia XMLStreamReader).
Por lo tanto, B primero verifica el estado anterior del análisis y luego maneja cada evento XML individual y luego almacena el estado (en un campo). El método A puede manejar los eventos XML todos a la vez accediendo al XMLStreamReader varias veces hasta que esté satisfecho.
Conclusión
StAX le permite estructurar su código de análisis (enlace de datos) de acuerdo con la estructura XML ; así que en relación con SAX, el 'estado' está implícito en el flujo del programa para StAX, mientras que en SAX, siempre es necesario preservar algún tipo de variable de estado + enrutar el flujo de acuerdo con ese estado, para la mayoría de las llamadas a eventos.
Recomiendo StAX para todos los documentos excepto los más simples. Más bien, muévase a SAX como una optimización más adelante (pero probablemente querrá volverse binario para entonces).
Siga este patrón al analizar con StAX:
public MyDataBindingObject parse(..) {
XMLStreamReader reader = ....;
do {
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
break;
}
} while(reader.hasNext());
MyDataBindingObject object = new MyDataBindingObject();
int level = 1;
do {
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
level++;
if(reader.getLocalName().equals("Whatever1")) {
WhateverObject child = parseSubTreeForWhatever(reader);
level --;
object.setWhatever(child);
}
if(level == 2) {
parseSubTreeForWhateverAtRelativeLevel2(reader);
level --;
object.setWhatever(child);
}
} else if(event == XMLStreamConstants.END_ELEMENT) {
level--;
}
} while(level > 0);
return object;
}
Entonces, el submétodo utiliza aproximadamente el mismo enfoque, es decir, nivel de conteo:
private MySubTreeObject parseSubTree(XMLStreamReader reader) throws XMLStreamException {
MySubTreeObject object = new MySubTreeObject();
int level = 1;
do {
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
level++;
if(reader.getLocalName().equals("Whatever2")) {
MyWhateverObject child = parseMySubelementTree(reader);
level --;
object.setWhatever(child);
}
if(level == 2) {
MyWhateverObject child = parseMySubelementTree(reader);
level --;
object.setWhatever(child);
}
} else if(event == XMLStreamConstants.END_ELEMENT) {
level--;
}
} while(level > 0);
return object;
}
Y luego, finalmente, alcanzas un nivel en el que leerás los tipos básicos.
private MySetterGetterObject parseSubTree(XMLStreamReader reader) throws XMLStreamException {
MySetterGetterObject myObject = new MySetterGetterObject();
int level = 1;
do {
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
level++;
if(reader.getLocalName().equals("FirstName")) {
String text = reader.getElementText()
if(text.length() > 0) {
myObject.setName(text)
}
level--;
} else if(reader.getLocalName().equals("LastName")) {
}
} else if(event == XMLStreamConstants.END_ELEMENT) {
level--;
}
} while(level > 0);
return myObject;
}
Esto es bastante sencillo y no hay lugar para malentendidos. Solo recuerde disminuir el nivel correctamente:
A. después de los caracteres esperados, pero obtuvo un END_ELEMENT en alguna etiqueta que debería contener caracteres (en el patrón anterior):
<Name>Thomas</Name>
fue en cambio
<Name></Name>
Lo mismo es cierto para un subárbol faltante también, entiendes la idea.
B. después de llamar a métodos de subanálisis, que se llaman en elementos de inicio, y devuelve DESPUÉS del elemento final correspondiente, es decir, el analizador está en un nivel más bajo que antes de la llamada al método (el patrón anterior).
Observe cómo este enfoque ignora totalmente los espacios en blanco 'ignorables' también, para una implementación más robusta.
Los analizadores
van con Woodstox para la mayoría de las funciones o Aaalto-xml para la velocidad.