2010-11-24 17 views
4

私はStAXを使用しようとしています(私はすでにそれを嫌っています....)
それを使用する唯一の方法はif-else条件の連続です。
しかし、最も重要なことは、解析対象のxml文書の 構造を事前に知っていない限り、要素をその子要素に関連付ける方法がないように思えるでしょう。これは正しいですか?私は次のことを試してみました
: 私は文字列java StAXを使って一般的な方法で子要素を取得する

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

でこのXMLを持っている私は、ブックの要素を取得するためのStAXを使用したいが、私が唯一、すべての構造をハードコードしているコードを書くことができそうです。
I. XMLEventReaderを使用して、一度 を取得すると、データ、作成者などのためにループが開始されます。
これには一般的な解決策がありますか?
私はこれをarroundにしようとしました:StringからXMLEventReaderに戻ってStringに戻そうとしましたが、もともと使用していたString表現を得ることができませんでした(名前空間は角括弧、余分なコロンなど)。

<?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を経由して対処することができます:

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

私は次の取得しますか?

答えて

5

私は本当にあなたが何をしようとして理解していないが、あなたはSTART_ELEMENTイベントの原因となっタグのローカル名をしたい場合、あなたはこのようにそれを行うことができます。

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

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

個人的には、私はたいていの場合、XMLStreamReaderが私にとって手頃であることがわかりますが、ユースケースや個人的な好みにもよると思います。プロのヒントは、スキーマが厳格であればあるほど、StAXを解析するデータが簡単になるということです。

JAX-Bを参照して、自動XMLデータバインディングを確認することもできます。

編集:このような

@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> 

} 

ライティングのもののみについてのコードがたくさん読みやすいとの理由ことはありませんが、:ここではOPでのXMLのためのナイーブ再帰下降パーサStAXのですまた、スタックがエラーをポップアップするときにトレースします。

+0

@gustafc:あなたが投稿するコードでは、要素が始まることがわかります。この要素のすべての子をどのように取得できますか?DOMは簡単です.StAXでどうやってやっていますか? – Cratylus

+0

まあ、すべてのイベントを要素の "内部"で取得するには、START_ELEMENTイベントよりももう1つのEND_ELEMENTが見つかるまでイベントを読み込むだけです。しかし、それは恐ろしいDOMishのやり方です。あなたが本当にやりたいことは、各要素を読み込んで、その要素をその場でいくつかのドメインオブジェクトに変換する再帰的な降下パーサーです。申し訳ありませんが、StAXを効果的に使用することを学ぶことは、主にDOM中毒を蹴る問題です。 – gustafc

+0

@gustafc:私は想像していました。「再帰的降下パーサ」と言うと、これはStAXを使用しないという意味ですか?だからStAXは "ハッキング"を使わないとこれを提供しないのですか? – Cratylus

1

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

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

関連する問題