Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find all nodes that have an attribute that matches a certain value with scala

Tags:

parsing

xml

scala

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

like image 623
ed. Avatar asked Sep 25 '09 13:09

ed.


3 Answers

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.

like image 113
Daniel C. Sobral Avatar answered Oct 04 '22 09:10

Daniel C. Sobral


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).

like image 20
Dmitry Stropalov Avatar answered Oct 04 '22 09:10

Dmitry Stropalov


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"

like image 35
user1003751 Avatar answered Oct 04 '22 11:10

user1003751