Scala:在 XML 中获取所有叶节点及其路径的最简单方法是什么?

Scala: What is the easiest way to get all leaf nodes and their paths in an XML?

我目前正在实现 xml 的 DFS 遍历,以便它到达每个叶节点并生成到叶节点的路径。

给定 XML:

<vehicles>
  <vehicle>
    gg
  </vehicle>
  <variable>
  </variable>
</vehicles>

输出(类似的东西):

Map("gg" -> "vehicles/vehicle", "" -> "vehicles/variable")

如果有可用的库可以执行此操作,那么我就不必维护代码了。

谢谢。感谢任何帮助。

这是一个使用标准 scala xml 库的解决方案,打印出路径图 -> "node text"

import scala.xml._               
val x = <div class="content"><a></a><p><q>hello</q></p><r><p>world</p></r><s></s></div>               
var map = Map[String,String]()               
def dfs(n: Seq[Node], brc: String): Unit = 
        n.foreach(x => {
                        if(x.child.isEmpty){
                           if(x.text == ""){ 
                            map = map + (brc + x.label -> "")
                            dfs(x.child,brc)
                          }
                          else{ 
                            map = map + (brc + x.label + " " -> x.text)
                            dfs(x.child,brc)
                          }
                        } 
                        else{ 
                          val bc = brc + x.label + ">"
                          dfs(x.child,bc)
                        }
                     }
               )               

dfs(x,"")
print(map) 

对于那些只想使用函数或更 XPath 友好的解决方案的人

我已经创建了一个存储库来扩展下面的代码并且应该生成正确的 XPath,但是我保留下面的代码原样,因为它相对简单并且是理解代码的良好起点。 repo is on github.

回答

这是一个受@Samar 的回答启发的实现,它生成 XPath(到目前为止没有正确的属性符号),是尾递归的,处理属性,并且不使用可变集合:

  /**
    * Helper function to add XPaths to a node sequence; assume a default of root nodes.
    */
  def pathifyNodes(nodes: Seq[Node], parPath: String = "/"): Seq[(Node, String)] =
    nodes.map{nn => (nn, parPath + nn.label + "/")}


  @tailrec
  final def uniqueXpaths(
    nodes: Seq[(Node, String)], pathData: List[(String, String)] = Nil
  ): List[(String, String)] = nodes match {
    case (node, currentPath) +: rest =>
      val newElementData =
        if(node.child.isEmpty) List((currentPath, node.text))
        else Nil
      val newAttributeData = node.attributes.asAttrMap.map{
        case (key, value) => (currentPath + "@" + key, value)
      }.toList
      uniqueXpaths(
        rest ++ node.child.flatMap(ns => pathifyNodes(ns, currentPath)),
        newElementData ::: newAttributeData ::: pathData
      )
    case Seq() => pathData
  }

运行 像这样:

    val x = <div class="content"><a></a><p><q>hello</q></p><r><p>world</p></r><s></s></div>
    val xpaOut = uniqueXpaths(pathifyNodes(x))

欢迎提出建议。我计划修复属性处理以生成依赖于属性选择的正确 XPath,并且还可能尝试以某种合理的方式处理递归 XPath,但我怀疑这会大大增加代码大小,所以我想继续并暂时粘贴它.