web-dev-qa-db-fra.com

Quand devrais-je choisir SAX plutôt que StAX?

Les analyseurs XML de flux tels que SAX et StAX sont plus rapides et plus efficaces en termes de mémoire que les analyseurs construisant une arborescence semblable à celle des analyseurs DOM. SAX est un analyseur Push, ce qui signifie qu'il s'agit d'une instance du modèle d'observateur (également appelé modèle d'écouteur). SAX était là le premier, mais ensuite est venu StAX - un analyseur Pull, ce qui signifie qu'il fonctionne comme un itérateur.

Vous pouvez trouver les raisons pour lesquelles préférer StAX à SAX partout, mais cela revient généralement à: "c'est plus facile à utiliser".

Dans le tutoriel Java sur JAXP, StAX est vaguement présenté comme le point central entre DOM et SAX: "c’est plus simple que SAX et plus efficace que DOM.". Cependant, je n’ai jamais trouvé d’indices indiquant que StAX aurait un rendement en mémoire plus lent ou moins efficace que SAX.

Tout cela m'a fait me demander: y a-t-il des raisons de choisir SAX au lieu de StAX?

71
Rinke

Pour généraliser un peu, I think StAX peut être aussi efficace que SAX. Avec la conception améliorée de StAXI, il est impossible de trouver une situation dans laquelle l'analyse SAX serait préférable, à moins de travailler avec du code hérité. 

EDIT: selon ce blog Java SAX contre StAXStAXoffer sans validation de schéma. 

17
Johan Sjöberg

Vue d'ensemble
Les documents XML sont des documents hiérarchiques, où les mêmes noms d'élément et espaces de nom peuvent apparaître à plusieurs endroits, avec une signification différente et une profondeur infinitive (récursive). Comme d'habitude, la solution aux gros problèmes consiste à les diviser en petits problèmes. Dans le contexte de l'analyse XML, cela signifie l'analyse de parties spécifiques de XML dans des méthodes spécifiques à ce XML. Par exemple, un élément de logique analyserait une adresse:

<Address>
    <Street>Odins vei</Street>    
    <Building>4</Building>
    <Door>b</Door>
</Address>

c'est-à-dire que vous auriez une méthode 

AddressType parseAddress(...); // A

ou 

void parseAddress(...); // B

quelque part dans votre logique, en prenant les arguments des entrées XML et en retournant un objet (le résultat de B peut être récupéré ultérieurement dans un champ).

SAXO
SAX "pousse" XML événements, vous laissant ainsi le soin de déterminer à quel endroit les événements XML appartiennent à votre programme/vos données.

// method in stock SAX handler
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException
    // .. your logic here for start element
}

Dans le cas d'un élément de début 'Building', vous devez déterminer que vous analysez réellement une adresse, puis acheminer l'événement XML à la méthode dont il est chargé d'interpréter Address.

StAX
StAX "extrait" XML événements, vous laissant ainsi le soin de déterminer où, dans votre programme/vos données, vous souhaitez recevoir les événements XML. 

// method in standard StAX reader
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
    // .. your logic here for start element
}

Bien sûr, vous voudriez toujours recevoir un événement 'Building' dans la méthode dont le travail consiste à interpréter Address. 

Discussion
La différence entre SAX et StAX est celle de Push and Pull. Dans les deux cas, l'état d'analyse doit être géré d'une manière ou d'une autre.

Cela se traduit par la méthode B typique de SAX et par la méthode A pour StAX. En outre, SAX doit donner des événements XML individuels à B, tandis que StAX peut donner à plusieurs événements A (en transmettant une instance de XMLStreamReader). 

Ainsi, B commence par vérifier l’état précédent de l’analyse, puis gère chaque événement XML individuel, puis stocke l’état (dans un champ). La méthode A peut simplement gérer les événements XML en une seule fois en accédant à XMLStreamReader plusieurs fois jusqu'à ce qu'elle soit satisfaite.

Conclusion
StAX vous permet de structurer votre code d'analyse syntaxique (liaison de données) selon la structure XML; Ainsi, en ce qui concerne SAX, l '"état" est implicite dans le flux de programme pour StAX, alors que dans SAX, vous devez toujours conserver une sorte de variable d'état + acheminer le flux en fonction de cet état, pour la plupart des appels d'événement. 

Je recommande StAX pour tous les documents, sauf les plus simples. Passez plutôt à SAX en tant qu'optimisation plus tard (mais vous voudrez probablement devenir binaire d'ici là).

Suivez ce modèle lorsque vous utilisez 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;
}

La sous-méthode utilise donc à peu près la même approche, à savoir le niveau de comptage:

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;
}

Et finalement, vous atteignez un niveau dans lequel vous lirez les types de base.

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;
}

C'est assez simple et il n'y a pas de place pour des malentendus. Rappelez-vous juste de décrémenter le niveau correctement:

A. après que vous attendiez des caractères mais que vous ayez un END_ELEMENT dans une balise qui devrait contenir des caractères (dans le modèle ci-dessus):

<Name>Thomas</Name>

était à la place

<Name></Name>

La même chose est vraie pour un sous-arbre manquant aussi, vous voyez l'idée.

B. après avoir appelé les méthodes de sous-sélection, appelées sur les éléments de départ, et renvoyé APRÈS l’élément de fin correspondant, c’est-à-dire que l’analyseur est à un niveau inférieur à celui utilisé avant l’appel de la méthode (le modèle ci-dessus).

Notez que cette approche ignore également totalement les espaces «ignorables», pour une mise en œuvre plus robuste.

Parseurs
Choisissez Woodstox pour la plupart des fonctionnalités ou Aaalto-xml pour la rapidité.

76
ThomasRS

@Rinke: Je pense que seul le temps je pense à préférer SAX à STAX au cas où vous n'auriez pas besoin de gérer/traiter le contenu XML; par exemple La seule chose que vous souhaitiez faire est de vérifier le bon format du XML entrant et de simplement gérer les erreurs si c'est le cas. Dans ce cas, vous pouvez simplement appeler la méthode parse () sur un analyseur SAX et spécifier un gestionnaire d'erreurs pour traiter tout problème d'analyse. Donc, STAX est fondamentalement le choix préférable dans les scénarios dans lesquels vous souhaitez gérer le contenu, car le gestionnaire de contenu SAX est trop difficile à coder ...

un exemple pratique de ce cas peut être si vous avez une série de noeuds SOAP dans votre système d'entreprise et qu'un noeud de niveau d'entrée SOAP ne laisse passer que le XML SOAP qui passe bien à l'étape suivante et qui sont bien forme, alors je ne vois aucune raison pour laquelle je voudrais utiliser STAX. Je voudrais juste utiliser SAX.

15
ag112

C'est tout un équilibre.

Vous pouvez transformer un analyseur SAX en un analyseur Pull en utilisant une file d'attente de blocage et quelques trucs de threads, donc, pour moi, il y a beaucoup moins de différence qu'il n'y parait.

Je pense qu’à l’heure actuelle, StAX doit être emballé dans un pot tiers, tandis que SAX est gratuit dans javax.

J'ai récemment choisi SAX et construit un analyseur Pull autour de ce dernier afin que je n'ai pas besoin de recourir à un bocal tiers.

Les futures versions de Java contiendront presque certainement une implémentation StAX, donc le problème disparaîtra.

1
OldCurmudgeon

StAX vous permet de créer des analyseurs syntaxiques XML bidirectionnels rapides. Il s'avère une meilleure alternative aux autres méthodes, telles que DOM et SAX, à la fois en termes de performances et de convivialité.

Vous pouvez en savoir plus sur StAX dans Tutoriels Java StAX

0