6

我希望有人会指出我在这里遗漏的一些明显的东西。我觉得我已经这样做了一百次了,今晚出于某种原因,由此产生的行为让我陷入了困境。

我正在从公共 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() 应该只返回一个节点列表。我添加了一点“instanceof”检查,果然,我得到了 Node 对象和 String 对象的组合。具体来说,不在同一行的实体内的行将作为字符串返回,即“This contains”和“and”。其他一切都是节点(如预期的那样)。

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

4

1 回答 1

7

我很确定这是正确的行为(尽管我一直发现 XmlSlurper 和 XmlParser 有古怪的 API)。你可以迭代的所有东西都应该实现一个节点接口 IMO 并且可能有一个typeTEXT可以用来知道从它们那里获取文本的东西。

这些文本节点是有效的节点,在许多情况下您会想要点击它们,因为它在 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
于 2012-12-20T05:40:09.727 回答