2012-12-20 37 views
5

我希望有人會指出我在這裏失蹤的事情。我覺得我已經完成了這一百次,而今晚由於某種原因,這種行爲正在讓我陷入一個循環。Groovy Node.depthFirst()返回一個節點和字符串列表?

我正在閱讀一些公共API的XML。我想從某個節點(「body」中的所有內容)中提取所有文本,其中還包括各種子節點。簡單的例子:

<xml> 
    <metadata> 
     <article> 
      <body> 
       <sec> 
        <title>A Title</title> 
        <p> 
         This contains 
         <italic>italics</italic> 
         and 
         <xref ref-type="bibr">xref's</xref> 
         . 
        </p> 
       </sec> 
       <sec> 
        <title>Second Title</title> 
       </sec> 
      </body> 
     </article> 
    </metadata> 
</xml> 

所以,最後我想遍歷樹所需的節點(再次,「體」)內提取包含在它的自然秩序中的所有文本。夠簡單了,所以我就寫了這個小Groovy腳本......

def xmlParser = new XmlParser() 
def xml = xmlParser.parseText(rawXml) 
xml.metadata.article.body[0].depthFirst().each { node -> 
    if(node.children().size() == 1) { 
     println node.text() 
    } 
} 

...它繼續進行炸燬「法無簽名:java.lang.String.children()」。所以我在想我自己:「等等,什麼?我瘋了嗎?」 Node.depthFirst()應該只返回一個節點列表。我添加了一個'instanceof'檢查,果然,我得到了一個N​​ode對象和String對象的組合。具體而言,不在同一行的實體內的行將以String的形式返回,即「This contains」和「and」。其他一切都是節點(如預期)。

我可以輕鬆解決這個問題。但是,這看起來不是正確的行爲,我希望有人能指出我的方向。

+0

據正如我所知道的那樣,Node.depthFirst的表現正如您在Groovy 1.7中期望的那樣。在groovy 2.0+中,我看到了Nodes/Strings的相同結果。 – Joseph

回答

7

我很確定這是正確的行爲(儘管我總是發現XmlSlurper和XmlParser具有扭曲的API)。所有你可以迭代的東西都應該實現一個節點接口IMO,並且可能有一個的TEXT,你可以用它來知道從中獲取文本。

這些文本節點是有效的節點,在許多情況下,您希望打開它,因爲它首先在XML中進行深度遍歷。如果它們沒有得到返回,那麼檢查1的子級大小是否會失效的算法會因爲某些節點(如<p>標記)在其下面混合了文本和元素。

此外,爲什麼depthFirst不一致地返回文本是唯一的孩子,如上面的italic所有的文本節點,使事情變得更糟。

我傾向於喜歡使用的常規方法的簽名,讓運行弄清楚這是這樣來處理每一個節點(而不是使用像instanceof)的正確方法:

def rawXml = """<xml> 
    <metadata> 
     <article> 
      <body> 
       <sec> 
        <title>A Title</title> 
        <p> 
         This contains 
         <italic>italics</italic> 
         and 
         <xref ref-type="bibr">xref's</xref> 
         . 
        </p> 
       </sec> 
       <sec> 
        <title>Second Title</title> 
       </sec> 
      </body> 
     </article> 
    </metadata> 
</xml>""" 

def processNode(String nodeText) { 
    return nodeText 
} 

def processNode(Object node) { 
    if(node.children().size() == 1) { 
     return node.text() 
    } 
} 

def xmlParser = new XmlParser() 
def xml = xmlParser.parseText(rawXml) 
def xmlText = xml.metadata.article.body[0].'**'.findResults { node -> 
    processNode(node) 
} 

println xmlText.join(" ") 

打印

A Title This contains italics and xref's . Second Title 

另外,在XmlSlurper類可能做更多你想/指望它什麼,有一個更合理的設置從text()方法的輸出。如果你真的不需要做任何形式的DOM與結果走(什麼XmlParser是「更好」),我建議XmlSlurper

def xmlParser = new XmlSlurper() 
def xml = xmlParser.parseText(rawXml) 
def bodyText = xml.metadata.article.body[0].text() 
println bodyText 

打印:

A Title 
        This contains 
        italics 
        and 
        xref's 
        . 
       Second Title 
+0

最後的珍聞是我真正想要的,正是我期待的。我可以發誓我嘗試過,但是我必須每次都將depthFirst()留在那裏,這會引發我一個循環。 – James

+0

哦,那和GPathResult.text()的行爲與Node.text()非常不同。我希望這些文檔能夠提供更多信息... – James

+0

您可以請您詳細說明雙「*」的含義 –