使用scala查找具有与特定值匹配的属性的所有节点

时间:2009-09-25 13:12:46

标签: xml scala parsing

我在Nabble上看到了以下示例,其中的目标是返回包含ID为X的属性的所有节点,其中包含值Y:

//find all nodes with an attribute "class" that contains the value "test"
val xml = XML.loadString( """<div>
<span class="test">hello</span>
<div class="test"><p>hello</p></div>
</div>""" )

def attributeEquals(name: String, value: String)(node: Node) = 
{ 
    node.attribute(name).filter(_==value).isDefined
}

val testResults = (xml \\ "_").filter(attributeEquals("class","test")) 
//prints: ArrayBuffer(
//<span class="test">hello</span>, 
//<div class="test"><p>hello</p></div>
//) 
println("testResults: " + testResults ) 

作为对此的扩展,如何执行以下操作:查找包含任何包含值Y的属性的所有节点:

//find all nodes with any attribute that contains the value "test"
val xml = XML.loadString( """<div>
 <span class="test">hello</span>
 <div id="test"><p>hello</p></div>
 <random any="test"/></div>""" )
 //should return: ArrayBuffer(
 //<span class="test">hello</span>, 
 //<div id="test"><p>hello</p></div>, 
 //<random any="test"/> )

我以为我可以像这样使用_:

val testResults = (xml \\ "_").filter(attributeEquals("_","test")) 

但它不起作用。我知道我可以使用模式匹配,但只是想看看我是否可以通过过滤做一些魔术。

干杯 - 埃德

5 个答案:

答案 0 :(得分:18)

首先,XML是Scala中的文字,所以:

val xml = <div><span class="test">hello</span><div class="test"><p>hello</p></div></div>

现在,关于问题:

def attributeValueEquals(value: String)(node: Node) = {
     node.attributes.exists(_.value.text == value)
}

事实上,我已经使用“exists”代替“filter”和“defined”来解决原始问题。

最后,我个人更喜欢运算符样式语法,特别是当你有一个准备好的函数而不是匿名函数时,要传递给“filter”:

val testResults = xml \\ "_" filter attributeValueEquals("test")

原作混合了“\\”的操作符样式和“filter”的点样式,最终非常难看。

答案 1 :(得分:3)

问题中的代码段不适用于Scala 2.8 - 由于此次比较

(_ == value)
需要替换为(_.text == value)(_ == Text(value))或更改值类型从字符串到文本。

在Daniel的回答中(_.value == value)需要替换为(_.value.text == value)

答案 2 :(得分:3)

之前的解决方案对我不起作用,因为它们都会查找匹配的任何值。如果你想查找带有值的特定属性,这是我的解决方案:

def getByAtt(e: Elem, att: String, value: String) = {
    def filterAtribute(node: Node, att: String, value: String) =  (node \ ("@" + att)).text == value   
    e \\ "_" filter { n=> filterAtribute(n, att, value)} 
}

然后

getByAtt(xml, "class", "test")

这将区分class="test""notclass="test"

答案 3 :(得分:0)

我对Scala很新,我建议你这个解决方案,但我不确定这是最好的解决方案:

def attributeValueEquals(value: String)(node: Node) = {
  node.attributes.foldLeft(false)((a : Boolean, x : MetaData) => a | (x.value == value))
}

val testResults = (xml \\ "_").filter(attributeValueEquals("test")) 
println("testResults: " + testResults )

// prints: testResults: ArrayBuffer(<span class="test">hello</span>, 
// <div id="test"><p>hello</p></div>, 
// <random any="test"></random>)

答案 4 :(得分:0)

def nodeHasValue(node:Node,value:String) = node.attributes.value != null && node.attributes.value.contains(value)

(x \\ "_").filter( nodeHasValue(_,"test"))
相关问题