4

StAX を使用しようとしています (もう嫌いです....)
それを使用する唯一の方法は、if-else 条件を連続して使用することです。
しかし、最も重要なことは、解析される xml ドキュメントの構造を事前に知っていない限り、要素をその子に関連付ける方法がないように思われることです.これは正しいですか?
私は次のことを試しました:私はこのxmlを文字列に持っています

<ns1:Root xmlns:ns1=\"http://rootNameSpace.com/\">
<ns1:A/>
<ns1:B>
        <Book xmlns=\"http://www.myNameSpace.com\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">
            <Data>
                <Author>John</Author>
                <Edition>1</Edition>
                <PubHouse>Small Publishing House</PubHouse>
                <Price>37.8</Price>
            </Data>
        </Book>
</ns1:B>
</ns1:Root>

StAX を使用して Book 要素を取得したいのですが、すべての構造をハードコーディングしたコードしか記述できないようです。
つまり、XMLEventReader を使用し、Book を取得したら、Data、Author などのループを開始
します。これに関する一般的な解決策はありますか?
これを回避するために次のことを試しました: String から XMLEventReader に移動して String に戻ろうとしましたが、最初に使用した正確な String 表現を取得できません (名前空間は角かっこ、余分なコロンなどで囲まれています)。

StringBuilder xml = new StringBuilder();
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
String msg = "<ns1:Root xmlns:ns1=\"http://rootNameSpace.com/\"><ns1:A/><ns1:B><Book xmlns=\"http://www.myNameSpace.com\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><Data><Author>John</Author><Edition>1</Edition><PubHouse>Small Publishing House</PubHouse><Price>37.8</Price></Data></Book></ns1:B></ns1:Root>";
InputStream input = new ByteArrayInputStream(msg.getBytes("UTF-8"));
XMLEventReader xmlEventReader = inputFactory.createXMLEventReader(input);
while (xmlEventReader.hasNext())
{

    XMLEvent event = xmlEventReader.nextEvent();
    StringWriter sw = new StringWriter();
    event.writeAsEncodedUnicode(sw);
   xml.append(sw);

}
System.out.println(xml);

私は以下を取得します:

<?xml version="1.0" encoding='UTF-8' standalone='no'?><['http://rootNameSpace.com/']:ns1:Root xmlns:ns1='http://rootNameSpace.com/'><['http://rootNameSpace.com/']:ns1:A></ns1:A><['http://rootNameSpace.com/']:ns1:B><['http://www.myNameSpace.com']::Book xmlns:='http://www.myNameSpace.com' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'><['http://www.myNameSpace.com']::Data><['http://www.myNameSpace.com']::Author>John</Author><['http://www.myNameSpace.com']::Edition>1</Edition><['http://www.myNameSpace.com']::PubHouse>Small Publishing House</PubHouse><['http://www.myNameSpace.com']::Price>37.8</Price></Data></Book></ns1:B></ns1:Root>

このケースは StAX または DOM で解決できますか?

4

2 に答える 2

5

何をしようとしているのかよくわかりませんが、START_ELEMENTイベントの原因となるタグのローカル名が必要な場合は、次のように実行できます。

if (event.getEventType() == START_ELEMENT) {
    QName qname = event.asStartElement().getName()
    System.out.println("Start of element " + qname.getLocalPart());
}

同様に、、asEndElementなどasCharactersは他のタイプのノードへのアクセスを提供します。

個人的にはXMLStreamReader、ほとんどの状況で私にとっては便利だと思いますが、それはユースケースやあなた自身の個人的な好みにもよると思います。プロのヒントは、スキーマが厳密であるほど、StAXでデータを解析しやすくなることです。

また、自動XMLデータバインディングについてはJAX-Bを確認することをお勧めします。

編集: OPのXML用のナイーブな再帰下降StAXパーサーは次のとおりです。

@Test
public void recursiveDescentStaxParser( ) throws XMLStreamException,
        FactoryConfigurationError
{
    String msg = "<ns1:Root xmlns:ns1=\"http://rootNameSpace.com/\"><ns1:A/><ns1:B><Book xmlns=\"http://www.myNameSpace.com\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><Data><Author>John</Author><Edition>1</Edition><PubHouse>Small Publishing House</PubHouse><Price>37.8</Price></Data></Book></ns1:B></ns1:Root>";
    XMLStreamReader reader = XMLInputFactory.newFactory( )
            .createXMLStreamReader( new StringReader( msg ) );

    reader.nextTag( );
    readRoot( reader );

}

private void readRoot( XMLStreamReader reader ) throws XMLStreamException
{
    while ( reader.nextTag( ) == XMLEvent.START_ELEMENT )
    {
        QName name = reader.getName( );
        if ( "B".equals( name.getLocalPart( ) ) )
            readBooks( reader );
        else
            reader.nextTag( ); // Empty <A>

    }
}

private void readBooks( XMLStreamReader reader ) throws XMLStreamException
{
    while ( reader.nextTag( ) == XMLEvent.START_ELEMENT )
    {
        QName name = reader.getName( );
        if ( !"Book".equals( name.getLocalPart( ) ) )
            throw new XMLStreamException( name.toString( ) );
        reader.nextTag( ); // Jump to <Data>
        readBook( reader );
        reader.nextTag( ); // Jump to </B>
    }
}

private void readBook( XMLStreamReader reader ) throws XMLStreamException
{
    reader.nextTag( ); // Skip to <Author>
    System.out.println( "Author: " + reader.getElementText( ) );
    reader.nextTag( ); // Skip to <Edition>
    System.out.println( "Edition: " + reader.getElementText( ) );
    reader.nextTag( ); // Skip to <PubHouse>
    System.out.println( "Publisher: " + reader.getElementText( ) );
    reader.nextTag( ); // Skip to <Price>
    System.out.println( "Price: " + reader.getElementText( ) );
    reader.nextTag( ); // Skip to </Book>

}

このようなものを書くと、コードが非常に読みやすくなり、推論が容易になるだけでなく、エラーが発生したときのスタックトレースも可能になります。

于 2010-11-24T08:44:07.560 に答える
1

ここで間違ったツールを選択したようです。Stax は、大きなコンテンツを効率的に処理するために使用する優れた API です。しかし、利便性が効率よりも重要である場合は、ツリー モデル (必ずしも DOM ではなく、たとえば XOM の方が優れている) またはデータ バインディング (JAXB または XStream) を検討する必要があります。具体的には、SAX のような Stax はストリームベースであるため、現在のイベントまたはトークンのみが表示されます。現在のストリームの位置を考慮すると、必ずしもアクセスできるとは限らないため、アクセスする方法が保証されていないため、子または親のアクセサーはありません。

ただし、パフォーマンスやメモリ使用量が懸念される場合は、JAXB (通常は DOM などのツリー モデルよりも効率的) またはStaxMateを検討できます。StaxMate は、Stax よりもパフォーマンスが高く、メモリ使用量が少ない拡張機能であり、使用するのが少し便利です。ドキュメントの順序で要素を反復処理する必要がありますが、そのカーソル アプローチは、親から子へのルックアップにより自然にマップされます。したがって、それはあなたのケースでうまくいくかもしれません。

于 2010-12-20T19:09:31.733 に答える