Groovy Node.depthFirst()返回节点和字符串列表?

时间:2012-12-20 03:54:12

标签: xml groovy xml-parsing xmlnode xmlslurper

我希望有人能指出我在这里遗漏的明显事物。我觉得我已经这样做了一百次,并且由于某种原因,今晚,这种行为让我陷入了困境。

我正在从公共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>

所以最终我想遍历所需节点内的树(再次,'body')并提取其自然顺序中包含的所有文本。很简单,所以我只写了这个小的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()应该只返回一个Node的List。我添加了一个'instanceof'检查,果然,我得到了Node对象和String对象的组合。具体而言,不在同一行中的实体内的行将作为String返回,即“This contains”和“and”。其他一切都是节点(如预期的那样)。

我可以轻松解决这个问题。然而,这似乎不是正确的行为,我希望有人可以指出我正确的方向。

1 个答案:

答案 0 :(得分:7)

我很确定这是正确的行为(虽然我总是发现XmlSlurper和XmlParser有棘手的API)。你可以迭代的所有东西都应该实现一个节点接口IMO,并且可能有一个type TEXT,你可以用来知道从它们获取文本。

这些文本节点是有效的节点,在许多情况下,您需要点击它,因为它首先通过XML进行深度遍历。如果它们没有被返回,那么检查子项大小为1的算法是否会起作用,因为某些节点(如<p>标签)下面都有混合文本和元素。

另外,为什么depthFirst不能始终返回文本是唯一子节点的所有文本节点,例如上面的italic,会使事情变得更糟。

我倾向于使用groovy方法的签名来让运行时找出处理每个节点的正确方法(而不是像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