如何使用scala.xml.Node遍历单个子节点

时间:2011-01-20 19:38:39

标签: scala xml-parsing

扩展了我之前问过的有关如何迭代scala.xml.Node中的节点集合的问题here

我想进一步采取这一步,并询问我如何在递归函数中查看前一个孩子,以便在遇到特定情况后获取值

例如(标记在这里)

<html>
    <head class="foo">
        <title>Welcome</title>
    </head>
    <body>
        <div>
            <p>Foo</p>
        </div>
    </body>
</html>

使用我当前的实现(感谢@ knut-arne-vedaa

def processNode(node: Node) {
  if (node.isInstanceOf[Text]) {
      if (node.text.contains("Welcome"))
      {
        //then inside here I want to go up to the prev element (head) and pull the class 
      }
    }
  node.child foreach processNode
}

我想添加另一个条件来从类部分

中获取文本“foo”

知道我可以在if语句中直接添加这个值吗?另外如何从此fx返回String值?一个简单的回归线或?

3 个答案:

答案 0 :(得分:4)

请注意,示例中Text节点的父节点实际上是&lt; title&gt;节点

由于节点没有对父母的引用,因此访问它们的一种方法就是在走树时随身携带它们,如下所示:

def processNode(node: Node, parent: Option[Node]) {
  if (node.isInstanceOf[Text]) {
      if (node.text.contains("Welcome"))
      {
        println("Node: " + node)
        println("Parent: " + parent.getOrElse("[toplevel]"))
      }
    }
  node.child foreach { n: Node => processNode(n, Some(node)) }
}
processNode(xml, None)

当然,当你想要将树重新爬回任意级别时,这会变得笨拙。一种方法是将节点包装在具有可选父引用的SuperNode中。

case class SuperNode(current: Node, parent: Option[SuperNode] = None)

为方便起见,在没有父节点的默认情况下,创建一个隐式函数将节点转换为超节点。

implicit def nodeMakeSuper(n: Node) = SuperNode(n)

现在,您可以在树上向上导航任意次数,如下所示:

def processNode(node: SuperNode) {
  node.current match {
      case n @ Text(t) if t.contains("Welcome") => {
          println("Node: " + n)
          node.parent match {
              case Some(p) => {
                  println("Parent: " + p.current)
                  p.parent match {
                      case Some(gp) => println("GrandParent: " + gp.current)
                      case None => println("No grandparent!")
                  }
              }
              case None => println("No parent!")
          }

      }
      case _ => // these aren't the droids you're looking for
  }
  node.current.child foreach { child: Node => processNode(SuperNode(child, Some(node))) }
}

答案 1 :(得分:2)

如果你继续朝着这个方向前进,你应该使用XML Zipper。我认为Scalaz有一个(它有一个,但我不确定你是否可以将它用于XML)。

答案 2 :(得分:1)

如果你想使用值 来做某些事情,如果它在具有特定属性的节点内,你可以反过来做:首先找到具有给定属性的所有节点,然后对它们进行相关处理。

您可以找到“class”属性为“foo”的所有节点,如下所示:

xml \\ "_" filter { _.attribute("class") == Some(Text("foo")) }
编辑:您可以这样使用它:

val markup = <html>...etc
val filtered = markup \\ "_" filter { _.attribute("class") == Some(Text("foo")) }
filtered map processNode

如果你想从处理中返回某些值,你需要采用不同的方式,但你的问题并不明确。