I saw the following example on Nabble, where the goal was to return all nodes that contain an attribute with an id of X that contains a value 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 )
As an extension to this how would one do the following: Find all nodes that contain any attribute that contains a value of 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"/> )
I was thinking I could use a _ like so:
val testResults = (xml \\ "_").filter(attributeEquals("_","test"))
But it doesn't work. I know I can use pattern matching, but just wanted to see if I could do some magic with the filtering.
Cheers - Ed
First, XML are literals in Scala, so:
val xml = <div><span class="test">hello</span><div class="test"><p>hello</p></div></div>
Now, as to the problem:
def attributeValueEquals(value: String)(node: Node) = {
node.attributes.exists(_.value.text == value)
}
In fact, I'd have used "exists
" instead of "filter
" and "defined
" for the original problem as well.
Finally, I personally prefer operator style syntax, particularly when you have a ready function, instead of an anonymous one, to pass to "filter
":
val testResults = xml \\ "_" filter attributeValueEquals("test")
The original mixes operator style for "\\
" and dot style for "filter
", which ends up quite ugly.
Code snippet in the question doesn't working with Scala 2.8 - due to this comparasion
(_ == value)
Needs to be replaced with (_.text == value)
or (_ == Text(value))
or change type of value from String to Text.
And in Daniel's answer (_.value == value)
needs to be replaced with (_.value.text == value)
.
The previous solutions didn't work for me because they all look for any value that matches. If you want to look for a particular attribute with a value, here is my solution:
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)}
}
And then
getByAtt(xml, "class", "test")
This will differentiate between class="test"
and "notclass="test"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With