¿Cuándo debería elegir SAX sobre StAX?

81

Los analizadores xml en streaming como SAX y StAX son más rápidos y más eficientes en memoria que los analizadores que construyen una estructura de árbol como los analizadores DOM. SAX es un analizador push, lo que significa que es una instancia del patrón de observador (también llamado patrón de escucha). SAX estuvo allí primero, pero luego vino StAX, un analizador de extracción, lo que significa que básicamente funciona como un iterador.

Puede encontrar razones por las que preferir StAX sobre SAX en todas partes, pero generalmente se reduce a: "es más fácil de usar".

En el tutorial de Java sobre JAXP, StAX se presenta vagamente como el medio entre DOM y SAX: "es más fácil que SAX y más eficiente que DOM". Sin embargo, nunca encontré ninguna pista de que StAX sería más lento o menos eficiente en memoria que SAX.

Todo esto me hizo preguntarme: ¿hay alguna razón para elegir SAX en lugar de StAX?

Rinke
fuente

Respuestas:

22

Para generalizar un poco, creo que StAXpuede ser tan eficiente como SAX. Con el diseño mejorado de StAX, realmente no puedo encontrar ninguna situación en la SAXque se prefiera el análisis, a menos que trabaje con código heredado.

EDITAR : Según este blog, Java SAX vs StAX StAX no ofrecen validación de esquema.

Johan Sjöberg
fuente
2
no es demasiado difícil agregar validación sobre stax. lo implementé yo mismo el otro día.
jtahlborn
Más detalles sobre la validación: stackoverflow.com/questions/5793087/stax-xml-validation
Ben
81

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(...); // A

o

void parseAddress(...); // B

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.

// method in standard StAX reader
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
    // .. your logic here for 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(..) { // provide input stream, reader, etc

        // set up parser
        // read the root tag to get to level 1
        XMLStreamReader reader = ....;

        do {
            int event = reader.next();
            if(event == XMLStreamConstants.START_ELEMENT) {
              // check if correct root tag
              break;
            }

            // add check for document end if you want to

        } while(reader.hasNext());

        MyDataBindingObject object = new MyDataBindingObject();
        // read root attributes if any

        int level = 1; // we are at level 1, since we have read the document header

        do {
            int event = reader.next();
            if(event == XMLStreamConstants.START_ELEMENT) {
                level++;
                // do stateful stuff here

                // for child logic:
                if(reader.getLocalName().equals("Whatever1")) {
                    WhateverObject child = parseSubTreeForWhatever(reader);
                    level --; // read from level 1 to 0 in submethod.

                    // do something with the result of subtree
                    object.setWhatever(child);
                }

                // alternatively, faster
                if(level == 2) {
                    parseSubTreeForWhateverAtRelativeLevel2(reader);
                    level --; // read from level 1 to 0 in submethod.

                    // do something with the result of subtree
                    object.setWhatever(child);
                }


            } else if(event == XMLStreamConstants.END_ELEMENT) {
                level--;
                // do stateful stuff here, too
            }

        } 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();
    // read element attributes if any

    int level = 1;
    do {
        int event = reader.next();
        if(event == XMLStreamConstants.START_ELEMENT) {
            level++;
            // do stateful stuff here

            // for child logic:
            if(reader.getLocalName().equals("Whatever2")) {
                MyWhateverObject child = parseMySubelementTree(reader);
                level --; // read from level 1 to 0 in submethod.

                // use subtree object somehow
                object.setWhatever(child);
            }

            // alternatively, faster, but less strict
            if(level == 2) {
              MyWhateverObject child = parseMySubelementTree(reader);
                level --; // read from level 1 to 0 in submethod.

                // use subtree object somehow
                object.setWhatever(child);
            }


        } else if(event == XMLStreamConstants.END_ELEMENT) {
            level--;
            // do stateful stuff here, too
        }

    } 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();
    // read element attributes if any

    int level = 1;
    do {
        int event = reader.next();
        if(event == XMLStreamConstants.START_ELEMENT) {
            level++;

            // assume <FirstName>Thomas</FirstName>:
            if(reader.getLocalName().equals("FirstName")) {
               // read tag contents
               String text = reader.getElementText()
               if(text.length() > 0) {
                    myObject.setName(text)
               }
               level--;

            } else if(reader.getLocalName().equals("LastName")) {
               // etc ..
            } 


        } else if(event == XMLStreamConstants.END_ELEMENT) {
            level--;
            // do stateful stuff here, too
        }

    } while(level > 0);

    // verify that all required fields in myObject are present

    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.

ThomasRS
fuente
En su declaración de apertura se lee "... mientras que en SAX ...". ¿Es esto un error tipográfico? ("SAX" en lugar de "StAX") En cualquier caso gracias por la respuesta. Si te entiendo correctamente, estás diciendo que el estado implícito en el enfoque SAX es un beneficio en comparación con la necesidad de rastrear la ubicación de tu árbol xml en el enfoque StAX.
Rinke
Gracias por la respuesta (ahora aún más elaborada). Me temo que todavía no veo cuál sería una buena razón para usar SAX en lugar de StAX. Su respuesta es una buena explicación de cómo funcionan ambos procesadores.
Rinke
Para documentos simples, son iguales. Mire, por ejemplo, este esquema: mpeg.chiariglione.org/technologies/mpeg-21/mp21-did/index.htm y StAX será más práctico.
ThomasRS
En pocas palabras, dado que ya está escribiendo su código, comprende qué parte del documento está analizando, es decir, toda la lógica para asignar un evento SAX al código correcto, se desperdicia.
ThomasRS
16

@Rinke: Supongo que es la única vez que pienso en preferir SAX sobre STAX en caso de que no necesite manejar / procesar contenido XML; por ejemplo, lo único que desea hacer es verificar el formato correcto del XML entrante y solo desea manejar los errores si tiene ... en este caso, simplemente puede llamar al método parse () en el analizador SAX y especificar el controlador de errores para manejar cualquier problema de análisis ... así que básicamente STAX es definitivamente una opción preferible en escenarios donde desea manejar contenido porque el controlador de contenido SAX es demasiado difícil de codificar ...

Un ejemplo práctico de este caso puede ser si tiene una serie de nodos SOAP en su sistema empresarial y un nodo SOAP de nivel de entrada solo permite que los XML SOAP pasen a la siguiente etapa que están bien formados, entonces no veo ninguna razón por la que usaría STAX. Solo usaría SAX.

ag112
fuente
Seleccioné esta respuesta como la mejor hasta ahora. Aunque es una buena respuesta, no creo que sea 100% autoritaria y clara. Las nuevas respuestas son bienvenidas.
Rinke
1

Todo es un equilibrio.

Puede convertir un analizador SAX en un analizador de extracción utilizando una cola de bloqueo y algunos trucos de subprocesos, por lo que, para mí, hay mucha menos diferencia de lo que parece al principio.

Creo que actualmente StAX debe empaquetarse a través de un jar de terceros, mientras que SAX viene gratis en javax.

Recientemente elegí SAX y construí un analizador de extracción a su alrededor para no tener que depender de un jar de terceros.

Es casi seguro que las versiones futuras de Java contendrán una implementación StAX para que el problema desaparezca.

ViejoCurmudgeon
fuente
1
Java SE 6 incluye StAX. Pero, por ejemplo, la implementación de Android no lo incluye.
Bjarne Boström
0

StAX le permite crear analizadores XML bidireccionales que son rápidos. Demuestra una mejor alternativa a otros métodos, como DOM y SAX, tanto en términos de rendimiento como de usabilidad.

Puede leer más sobre StAX en Java Tutoriales de StAX

Annamalai Thangaraj
fuente
-1

La mayoría de la información proporcionada por esas respuestas está algo desactualizada ... se ha realizado un estudio exhaustivo de todas las bibliotecas de análisis XML en este artículo de investigación de 2013 ... léalo y verá fácilmente el claro ganador (pista: solo hay una verdadero ganador) ...

http://recipp.ipp.pt/bitstream/10400.22/1847/1/ART_BrunoOliveira_2013.pdf

vtd-xml-autor
fuente
1
Leí el artículo, el ganador es StAX usando la API del cursor como en XMLStreamReader.
Roland
muy divertido :), que quiere decir el ganador de la carrera de tortuga :)
ETV-xml-autor
Acabo de volver a leer el artículo y sí, StaX es superior a vtd, más rápido y con menos consumo de memoria. Entonces ¿cual es tu punto?
Roland
EL GANADOR ES STAX ¿De qué manera? ¿A qué parte del artículo te refieres? modificando documento, o seleccionando o diferenciando? aparentemente, el autor del artículo llegó a una conclusión diferente. pero podrían estar totalmente equivocados ...
vtd-xml-author
1
Ej. página 80: Según los resultados (figura 11 y figura 12) podemos ver que StAX es la API que tiene el mejor rendimiento, seguida de VTD. Sin embargo, VTD consume una cantidad considerable de memoria. El consumo de memoria puede ser un cuello de botella para los entornos que ofrecen capacidades limitadas.
Roland