83

SAXやStAXのようなストリーミングxmlパーサーは、DOMパーサーのようなツリー構造を構築するパーサーよりも高速でメモリ効率が高くなります。SAXはプッシュパーサーです。つまり、オブザーバーパターン(リスナーパターンとも呼ばれます)のインスタンスです。SAXが最初にありましたが、次にStAX(プルパーサー)が登場しました。これは、基本的にイテレーターのように機能することを意味します。

どこでもSAXよりもStAXを好む理由を見つけることができますが、それは通常、「使いやすい」ということになります。

JAXPに関するJavaチュートリアルでは、StAXはDOMとSAXの中間として漠然と示されています。「SAXよりも簡単で、DOMよりも効率的です」。ただし、StAXがSAXよりも遅い、またはメモリ効率が低いという手がかりは見つかりませんでした。

このすべてが私に不思議に思いました:StAXの代わりにSAXを選ぶ理由はありますか?

4

6 に答える 6

84

概要
XMLドキュメントは階層的なドキュメントであり、同じ要素名と名前空間が複数の場所で発生し、意味が異なり、深さが無限に(再帰的に)発生する可能性があります。通常のように、大きな問題の解決策は、それらを小さな問題に分割することです。XML解析のコンテキストでは、これは、そのXMLに固有のメソッドでXMLの特定の部分を解析することを意味します。たとえば、1つのロジックがアドレスを解析します。

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

つまり、あなたは方法を持っているでしょう

AddressType parseAddress(...); // A

また

void parseAddress(...); // B

ロジックのどこかで、XML入力引数を取り、オブジェクトを返します(Bの結果は後でフィールドからフェッチできます)。

SAX SAXはXMLイベント
を「プッシュ」し、XMLイベントがプログラム/データのどこに属するかを決定するのはあなたに任されています。

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

'Building' start要素の場合、実際にAddressを解析していることを確認してから、Addressを解釈するジョブを持つメソッドにXMLイベントをルーティングする必要があります。

StAX StAXはXMLイベント
を「プル」し、プログラム/データのどこでXMLイベントを受信するかを決定するのはあなたに任されています。

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

もちろん、Addressを解釈することがジョブであるメソッドで「Building」イベントを常に受信する必要があります。

考察
SAXとStAXの違いは、プッシュとプルの違いです。どちらの場合も、解析状態は何らかの方法で処理する必要があります。

これは、SAXでは一般的な方法Bに、StAXでは方法Aに変換されます。さらに、SAXはB個の個別のXMLイベントを提供する必要がありますが、StAXは(XMLStreamReaderインスタンスを渡すことによって)A個の複数のイベントを提供できます。

したがって、Bは最初に解析の前の状態をチェックし、次に個々のXMLイベントを処理してから、状態を(フィールドに)格納します。方法Aは、満足するまでXMLStreamReaderに複数回アクセスすることにより、XMLイベントを一度に処理できます。

結論
StAXを使用すると、XML構造に従って解析(データバインディング)コードを構造化できます。したがって、SAXに関連して、「状態」はStAXのプログラムフローから暗黙的に示されますが、SAXでは、ほとんどのイベント呼び出しで、常に何らかの状態変数を保持し、その状態に従ってフローをルーティングする必要があります。

最も単純なドキュメントを除くすべてのドキュメントにStAXをお勧めします。後で最適化としてSAXに移行します(ただし、それまでにバイナリに移行することをお勧めします)。

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

したがって、サブメソッドはほぼ同じアプローチ、つまりレベルのカウントを使用します。

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

そして、最終的には、基本タイプを読み取るレベルに到達します。

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

これは非常に簡単で、誤解の余地はありません。レベルを正しくデクリメントすることを忘れないでください:

A.文字を期待したが、文字を含む必要があるタグにEND_ELEMENTを取得した後(上記のパターン):

<Name>Thomas</Name>

代わりに

<Name></Name>

欠落しているサブツリーについても同じことが言えます。

B.開始要素で呼び出されるサブ解析メソッドを呼び出した後、対応する終了要素の後に戻ります。つまり、パーサーはメソッド呼び出しの前よりも1レベル低くなります(上記のパターン)。

より堅牢な実装のために、このアプローチが「無視できる」空白も完全に無視することに注意してください。

パーサーは、ほとんどの機能についてはWoodstoxを使用し
速度についてはAaalto-xmlを使用します。

于 2011-09-23T01:51:19.223 に答える
22

少し一般化すると、私StAXはと同じくらい効率的であると思いますSAX。の改善された設計では、レガシーコードを使用しない限り、解析が優先されるStAX状況を実際に見つけることはできません。SAX

編集:このブログによると、JavaSAXとStAX StAXはスキーマ検証を提供していません。

于 2011-09-22T21:48:35.087 に答える
16

@Rinke:XMLコンテンツを処理/処理する必要がない場合に備えて、STAXよりもSAXを優先することを考えるのは時間だけだと思います。たとえば、必要なのは、着信XMLの整形式性をチェックし、エラーが発生した場合はそれを処理することだけです...この場合、SAXパーサーでparse()メソッドを呼び出し、エラーハンドラーを指定して解析の問題....基本的に、SAXコンテンツハンドラーのコーディングが難しすぎるため、コンテンツを処理するシナリオでは、STAXが間違いなく好ましい選択です...

このケースの実際的な例の1つは、エンタープライズシステムに一連のSOAPノードがあり、エントリレベルのSOAPノードがそれらのSOAPXMLを整形式である次のステージにのみ通過させる場合です。 STAXを使用します。SAXを使うだけです。

于 2011-10-06T07:41:46.097 に答える
1

それはすべてバランスです。

ブロッキングキューといくつかのスレッドトリックを使用して、SAXパーサーをプルパーサーに変えることができるので、私には、最初に思われるよりもはるかに少ない違いがあります。

現在、SAXがjavaxで無料で提供されている間、StAXはサードパーティのjarを介してパッケージ化する必要があると思います。

私は最近SAXを選択し、その周りにプルパーサーを構築したので、サードパーティのjarに依存する必要はありませんでした。

Javaの将来のバージョンには、ほぼ確実にStAX実装が含まれるため、問題は解消されます。

于 2011-10-10T13:49:15.810 に答える
0

StAXを使用すると、高速な双方向XMLパーサーを作成できます。パフォーマンスと使いやすさの両方の点で、DOMやSAXなどの他の方法よりも優れた代替手段であることが証明されています。

StAXの詳細については、JavaStAXチュートリアルを参照してください。

于 2015-04-01T09:59:05.883 に答える
-1

これらの回答によって提供される情報のほとんどはやや時代遅れです...この2013年の研究論文ではすべてのXML解析ライブラリの包括的な研究がありました...それを読むと、明確な勝者が簡単にわかります(ヒント:1つだけです真の勝者)..。

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

于 2016-04-19T20:26:25.973 に答える