使用 Scala 模式匹配提取具有特定名称的 XML 个元素,而不考虑内容
Using Scala pattern matching to extract XML elements with a certain name, regardless of content
给定以下 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
})
不过好像还有更巧妙的办法。
可以使用Elem
对象来匹配:
nodes collect { case b @ Elem(_, "bar", _, _, _*) => b }
Elem
的来源是here,所以可以看到unapplySeq
的定义。消息来源甚至有评论:
It is possible to deconstruct any Node
instance (that is not a SpecialNode
or a Group
) using the syntax 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
.
给定以下 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
})
不过好像还有更巧妙的办法。
可以使用Elem
对象来匹配:
nodes collect { case b @ Elem(_, "bar", _, _, _*) => b }
Elem
的来源是here,所以可以看到unapplySeq
的定义。消息来源甚至有评论:
It is possible to deconstruct any
Node
instance (that is not aSpecialNode
or aGroup
) using the syntaxcase 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
.