2010-11-24 46 views
4

我正在嘗試使用StAX(我已經不喜歡它了......)
似乎使用它的唯一方法是通過連續的if-else條件。
但最重要的是,似乎沒有辦法將一個元素與其子元素相關聯,除非事先知道要解析的xml文檔的 結構。是否正確?
我曾嘗試以下: 我有這樣的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> 

我想用的StAX得到Book元素,但似乎我只能寫已經硬編碼所有的結構代碼。
I.e.使用XMLEventReader和一次 你得到書,開始循環數據,作者等
有沒有一個通用的解決方案呢?
我嘗試了以下方法來解決這個問題:我嘗試從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來解決是唯一的解決辦法?

回答

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數據綁定。

編輯:這裏有一個天真的遞歸下降的StAX解析器在OP的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> 

} 

寫這樣的東西不僅使代碼更容易閱讀和推理,但當彈出錯誤時也會跟蹤堆棧。

+0

@gustafc:通過你發佈的代碼,我知道一個元素開始了。如何獲得這個元素的所有子元素?使用DOM是微不足道的。如何使用StAX做到這一點? – Cratylus 2010-11-24 08:56:30

1

聽起來你可能在這裏選擇了錯誤的工具:Stax是一個很棒的API,可用於高效處理大型內容。但是,如果方便比效率更重要,是的,您可能應該考慮一個樹模型(不一定是DOM,比如XOM更好)或數據綁定(JAXB或XStream)。特別是,像SAX這樣的Stax是基於流的,所以你只能看到當前的事件或令牌。沒有兒童或父母的訪問者,因爲沒有保證的方式可以到達他們,因爲考慮到當前流的位置,這不一定是可能的。

但是,如果需要關注性能或內存使用情況,您仍然可以考慮JAXB(通常比DOM之類的樹模型更高效)或StaxMate。StaxMate是Stax的高性能,低內存使用擴展,使用起來更方便。 儘管您仍然需要按照文檔順序迭代元素,但其光標方法會更自然地與父子查找相匹配。所以它可能適用於你的情況。