it-swarm.dev

Kdy mám vybrat SAX přes StAX?

Streamování XML-parserů jako SAX a StAX je rychlejší a efektivnější paměti než parsery vytvářející stromovou strukturu jako DOM-parsery. SAX je Push parser, což znamená, že jde o instanci vzoru pozorovatele (nazývaného také vzor posluchače). SAX tam byl první, ale pak přišel StAX - pull parser, což znamená, že v podstatě funguje jako iterátor.

Důvody, proč dáváte přednost StAXu před SAXem, naleznete všude, ale obvykle se sníží na: „je to jednodušší na používání“.

V tutoriálu Java na JAXP je StAX nejasně prezentován jako prostředník mezi DOM a SAX: "Je to jednodušší než SAX a efektivnější než DOM". Nikdy jsem však nenašel žádné vodítko, že by StAX byl pomalejší nebo méně efektivní než SAX.

Tohle mě děsilo: existují nějaké důvody pro výběr SAX místo StAX?

71
Rinke

Zobecnit trochu, I myslet StAX může být stejně efektivní jako SAX. Se zlepšeným designem StAXI nelze skutečně najít žádnou situaci, kdy by SAX parsování bylo upřednostňováno, pokud nebude pracovat s původním kódem. 

EDITOVAT: Podle tohoto blogu Java SAX vs. StAXStAXffer žádné ověření schématu. 

17
Johan Sjöberg

Přehled
Dokumenty XML jsou hierarchické dokumenty, kde stejné názvy prvků a jmenné prostory se mohou vyskytovat na několika místech s odlišným významem a v infinitivní hloubce (rekurzivní). Jako obvykle je řešením velkých problémů jejich rozdělení na malé problémy. V kontextu analýzy XML to znamená analyzovat specifické části XML v metodách specifických pro tento XML. Jedna logika by například analyzovala adresu:

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

měli byste metodu 

AddressType parseAddress(...); // A

nebo 

void parseAddress(...); // B

někde ve vaší logice, brát argumenty XML a vrátit objekt (výsledek B může být načten z pole později).

SAXOFON
SAX 'posune' XML události, takže je na vás, abyste určili, kde události XML patří do vašeho programu/dat.

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

V případě spouštěcího prvku „Budova“ je třeba určit, zda ve skutečnosti analyzujete adresu a poté směrovat událost XML na metodu, jejíž úlohou je interpretovat adresu.

StAX
StAX 'táhne' XML události a ponechává na vás, abyste určili, kde ve vašem programu/datech přijímáte události XML. 

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

Samozřejmě byste vždy chtěli obdržet událost „Building“ v metodě, jejíž úkolem je interpretovat adresu. 

Diskuse
Rozdíl mezi SAX a StAX je u Push a pull. V obou případech musí být analyzován stav nějakým způsobem.

To se týká metody B typické pro SAX a metody A pro StAX. Navíc musí SAX dát B jednotlivé události XML, zatímco StAX může dát A více událostí (předáním instance XMLStreamReader). 

B tedy nejprve zkontroluje předchozí stav analýzy a poté zpracová každou jednotlivou událost XML a poté uloží stav (do pole). Metoda A může pouze zpracovat události XML najednou přístupem k XMLStreamReader několikrát, dokud není splněna.

Závěr
StAX umožňuje strukturovat kód analýzy (vazby dat) podle struktury XML; tak ve vztahu k SAX, 'stav' je implicitní z toku programu pro StAX, zatímco v SAX, vždy potřebujete zachovat nějaký druh stavové proměnné + směr toku podle tohoto stavu, pro většinu volání událostí. 

Doporučuji StAX pro všechny nejjednodušší dokumenty. Později se přesuňte na SAX jako optimalizaci později (ale budete pravděpodobně chtít binárně do té doby).

Při analýze pomocí funkce StAX postupujte podle tohoto vzoru:

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

Submetoda tedy používá přibližně stejný přístup, tj. Úroveň počítání:

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

A pak nakonec dosáhnete úrovně, ve které budete číst základní typy.

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

To je poměrně jednoduché a není zde prostor pro nedorozumění. Jen nezapomeňte správně snížit úroveň:

A. poté, co jste očekávali znaky, ale dostali jste END_ELEMENT v nějaké značce, která by měla obsahovat znaky (ve výše uvedeném vzoru):

<Name>Thomas</Name>

místo toho

<Name></Name>

Totéž platí i pro chybějící podstrom.

B. po zavolání způsobů dílčího zpracování, které jsou vyvolány na startovacích prvcích, a vrátí se po odpovídajícím koncovém prvku, tj. Syntaktický analyzátor je o jednu úroveň nižší než před voláním metody (výše uvedený vzor).

Všimněte si, že tento přístup také zcela ignoruje „neznatelný“ mezerový prostor, a to pro robustnější implementaci.

Parsers
Jděte s Woodstox pro většinu funkcí nebo Aaalto-xml pro rychlost.

76
ThomasRS

@ Rinke: Myslím, že jen čas si myslím, že preferuji SAX přes STAX v případě, když nepotřebujete zpracovávat/zpracovávat obsah XML; pro např. Jediné, co chcete udělat, je zkontrolovat správnost formování příchozího XML a chci pouze zpracovat chyby, pokud má ... v tomto případě můžete jednoduše zavolat metodu parse () na syntaktický analyzátor SAX a specifikovat obslužný program chyby pro zpracování jakéhokoliv problému analýzy .... tak v podstatě STAX je rozhodně preferovanou volbou ve scénářích, kde chcete zpracovávat obsah, protože popisovač obsahu SAX je příliš obtížný kódovat ...

jedním praktickým příkladem tohoto případu může být, pokud máte ve vašem podnikovém systému řadu uzlů SOAP a uzel vstupní úrovně SOAP umožňuje pouze těm SOAP XML projít další fází, které jsou dobře formality, pak nevidím žádný důvod, proč bych použil STAX. Použil bych jen SAX.

15
ag112

Všechno je to rovnováha.

Analyzátor SAX můžete zapnout do parseru pull pomocí blokovací fronty a nějakého triku, takže pro mě je mnohem menší rozdíl, než se zdá.

Věřím, že v současné době musí být StAX zabalen do sklenice třetí strany, zatímco SAX je zdarma v javaxu.

Nedávno jsem si vybral SAX a postavil kolem něj parser, takže jsem se nemusel spoléhat na jaro třetí strany.

Budoucí verze Java budou téměř jistě obsahovat implementaci StAX, takže problém zmizí.

1
OldCurmudgeon

StAX umožňuje vytvářet obousměrné analyzátory XML, které jsou rychlé. To dokazuje lepší alternativu k jiným metodám, jako je DOM a SAX, a to jak z hlediska výkonu, tak i použitelnosti

Více o StAXu si můžete přečíst v Java StAX Návody

0