给出以下XML元素 -
val nodes = List(
<foo/>,
<bar/>,
<baz/>,
<bar>qux</bar>,
<bar quux="corge"/>,
<bar quux="grauply">waldo</bar>,
<bar quux="fred"></bar>
)
- 如何构建一个匹配所有<bar/>
的模式?我试过了,例如:
nodes flatMap (_ match {
case b @ <bar/> => Some(b)
case _ => None
})
但这只与空箱匹配。
res17: List[scala.xml.Elem] = List(<bar/>, <bar quux="corge"/>, <bar quux="fred"></bar>)
如果我允许内容占位符:
nodes flatMap (_ match {
case b @ <bar>{content}</bar> => Some(b)
case _ => None
})
这只匹配非清空。
res20: List[scala.xml.Elem] = List(<bar>qux</bar>, <bar quux="grauply">waldo</bar>)
我当然可以放弃XML文字而只写
nodes flatMap (_ match {
case e: Elem if e.label == "bar" => Some(e)
case _ => None
})
但似乎必须有一种更聪明的方式。
答案 0 :(得分:4)
您可以使用Elem
对象进行匹配:
nodes collect { case b @ Elem(_, "bar", _, _, _*) => b }
Elem
的来源为here,因此您可以看到unapplySeq
的定义。消息来源甚至有评论:
可以使用语法
Node
解析任何SpecialNode
个实例(不是Group
或case Elem(prefix, label, attribs, scope, child @ _*) => ...
)
另一种方法是使用pattern alternatives:
nodes collect { case b @ (<bar/> | <bar>{_}</bar>) => b }
请注意,模式替代项不能绑定除通配符之外的变量。
如果这是您的常用操作,那么您可以考虑编写自己的提取器(如文档here所述)。例如:
object ElemLabel {
def unapply(elem: Elem): Option[String] = Some(elem.label)
}
然后:
nodes collect { case b @ ElemLabel("bar") => b }
当然,在您提供的示例中,您只是过滤,在这种情况下:
nodes filter { _.label == "bar" }
就足够了,可能是你最好的选择。即使您计划在过滤器之后执行其他操作,并且您关注性能和构建中间集合,也可以使用view
并避免此问题。
另请注意,collect
始终使用flatMap
,这是一种更加惯用的方法,可以使用match
,Option
进行过滤,映射和匹配,以及{{1}}。