it-swarm.dev

Quando devo escolher SAX over StAX?

Os analisadores de fluxo xml, como o SAX e o StAX, são mais rápidos e mais eficientes na memória do que os analisadores criando uma estrutura em árvore, como os parsers do DOM. O SAX é um analisador de push, o que significa que é uma instância do padrão de observador (também chamado de padrão de ouvinte). O SAX estava lá primeiro, mas depois veio o StAX - um analisador de pull, o que significa que ele basicamente funciona como um iterador.

Você pode encontrar razões para preferir o StAX over SAX em todos os lugares, mas geralmente se resume a: "é mais fácil de usar".

No tutorial Java no JAXP StAX é vagamente apresentado como o meio entre DOM e SAX: "é mais fácil que o SAX e mais eficiente que o DOM". No entanto, nunca encontrei pistas de que o StAX seria mais lento ou menos eficiente de memória que o SAX.

Tudo isso me fez pensar: Há alguma razão para escolher o SAX em vez do StAX?

71
Rinke

Para generalizar um pouco, I acha que StAX pode ser tão eficiente quanto SAX. Com o design aprimorado de StAXI não é possível encontrar qualquer situação em que a análise de SAX seja preferida, a menos que esteja trabalhando com código legado. 

EDIT: De acordo com este blog Java SAX vs. StAXStAXoffer sem validação de esquema. 

17
Johan Sjöberg

Visão global
Documentos XML são documentos hierárquicos, em que os mesmos nomes de elementos e namespaces podem ocorrer em vários locais, com significado diferente e em profundidade infinitiva (recursiva). Como de costume, a solução para grandes problemas é dividi-los em pequenos problemas. No contexto da análise XML, isso significa analisar partes específicas de XML em métodos específicos para esse XML. Por exemplo, uma parte da lógica analisaria um endereço:

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

ou seja, você teria um método 

AddressType parseAddress(...); // A

ou 

void parseAddress(...); // B

em algum lugar na sua lógica, pegando argumentos de entradas XML e retornando um objeto (o resultado de B pode ser buscado em um campo mais tarde).

SAXOFONE
SAX 'empurra' XML events, deixando para você determinar onde os eventos XML pertencem ao seu programa/dados.

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

No caso de um elemento start 'Building', você precisaria determinar que está realmente analisando um Address e, em seguida, rotear o evento XML para o método cuja tarefa é interpretar Address.

StAX
StAX 'puxa' XML events, deixando a você para determinar onde em seu programa/dados para receber os eventos XML. 

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

Naturalmente, você sempre deseja receber um evento 'Building' no método cujo trabalho é interpretar Address. 

Discussão
A diferença entre SAX e StAX é a de empurrar e puxar. Em ambos os casos, o estado de análise deve ser tratado de alguma forma.

Isso se traduz no método B como típico para SAX e no método A para StAX. Além disso, o SAX deve fornecer eventos XML individuais B, enquanto o StAX pode fornecer múltiplos eventos A (passando uma instância XMLStreamReader). 

Assim, B primeiro verificar o estado anterior da análise e, em seguida, lidar com cada evento XML individual e, em seguida, armazenar o estado (em um campo). O método A pode manipular os eventos XML de uma só vez, acessando o XMLStreamReader várias vezes até que seja satisfeito.

Conclusão
StAX permite estruturar seu código de análise (vinculação de dados) de acordo com a estrutura XML; assim, em relação ao SAX, o 'estado' é implícito do fluxo de programa para StAX, enquanto no SAX, você sempre precisa preservar algum tipo de variável de estado + rotear o fluxo de acordo com esse estado, para a maioria das chamadas de evento. 

Eu recomendo StAX para todos, mas os documentos mais simples. Em vez disso, vá para o SAX como uma otimização mais tarde (mas você provavelmente vai querer ir binário até lá).

Siga este padrão ao analisar usando o 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;
}

Assim, o método usa a mesma abordagem, ou seja, o nível de contagem:

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

E então, eventualmente, você atinge um nível no qual você vai ler os 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;
}

Isto é bastante simples e não há espaço para mal-entendidos. Apenas lembre-se de diminuir o nível corretamente:

A. depois que você esperou caracteres, mas tem um END_ELEMENT em alguma tag que deve conter chars (no padrão acima):

<Name>Thomas</Name>

foi em vez disso

<Name></Name>

O mesmo é verdade para uma subárvore ausente também, você entendeu.

B. depois de chamar os métodos subparsing, que são chamados nos elementos start, e retorna AFTER o elemento final correspondente, ou seja, o analisador está em um nível mais baixo do que antes da chamada do método (o padrão acima).

Observe como essa abordagem ignora totalmente os espaços em branco 'ignoráveis' também, para uma implementação mais robusta.

Parsers
Vá com Woodstox para a maioria dos recursos ou Aaalto-xml para velocidade.

76
ThomasRS

@Rinke: Acho que só na hora em que penso em preferir o SAX ao STAX no caso de você não precisar manipular/processar o conteúdo XML; por ex. A única coisa que você quer fazer é verificar o bom estado do XML recebido e apenas tratar os erros se ele tiver ... nesse caso, você pode simplesmente chamar o método parse () no analisador SAX e especificar o manipulador de erros para manipular qualquer problema de análise. .... então, basicamente, STAX é definitivamente a escolha preferível em cenários onde você deseja manipular o conteúdo, porque o manipulador de conteúdo SAX é muito difícil de codificar ...

um exemplo prático deste caso pode ser se você tiver uma série de nós SOAP em seu sistema corporativo e um nó de nível de entrada SOAP permitir que esses XML SOAP passem para o próximo estágio, que são bem forma, então eu não vejo qualquer razão pela qual eu usaria STAX. Eu apenas usaria o SAX.

15
ag112

Tudo é um equilíbrio.

Você pode transformar um analisador SAX em um analisador de pull usando uma fila de bloqueio e algum truque de thread, então, para mim, há muito menos diferença do que parece pela primeira vez.

Eu acredito que atualmente o StAX precisa ser empacotado através de um jar de terceiros enquanto o SAX vem livre no javax.

Eu recentemente escolhi o SAX e construí um analisador de pull ao redor dele, então não precisei depender de um jar de terceiros.

Futuras versões do Java quase certamente conterão uma implementação StAX, então o problema desaparece.

1
OldCurmudgeon

O StAX permite criar analisadores XML bidirecionais que são rápidos. Isso é uma alternativa melhor para outros métodos, como DOM e SAX, tanto em termos de desempenho quanto de usabilidade.

Você pode ler mais sobre o StAX em Java StAX Tutorials

0
Annamalai Thangaraj