2009-09-25 16 views
11

Vi el siguiente ejemplo en Nabble, donde el objetivo era devolver todos los nodos que contienen un atributo con una identificación de X que contiene un valor Y:Buscar todos los nodos que tengan un atributo que coincida con un cierto valor con scala

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

como una extensión a este ¿cómo se haga lo siguiente: Encontrar todos los nodos que contienen cualquier atributo que contiene un valor de 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"/>) 

estaba pensando que podría utilizar una _ este modo:

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

Pero no funciona. Sé que puedo usar la coincidencia de patrones, pero solo quería ver si podía hacer algo de magia con el filtrado.

Saludos - Ed

+0

encontré lenguaje XPath es más simple https://github.com/nrinaudo/kantan.xpath y es tiempo de compilación seguro en Scala –

Respuesta

18

En primer lugar, XML son literales en Scala, por lo que:

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

Ahora, en cuanto al problema:

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

De hecho, habría usado "exists" en lugar de "filter" y "defined" para el problema original también.

Por último, yo personalmente prefiero sintaxis de estilo operador, sobre todo cuando se tiene una función de lista, en lugar de un anónimo, para pasar a "filter ':

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

El estilo operador mezclas originales para' \\" y estilo de punto para "filter", que termina siendo bastante feo.

+0

si usa scala 2.9.1, consulte esta respuesta http://stackoverflow.com/questions/3819485/in-regards-of-finding-all-nodes-that-have-an-attribute-that-matches- a-certain-va (esta respuesta seleccionada devuelve un NodeSeq() en 2.9.1 – fmpwizard

0

estoy bastante nuevo en Scala, os propongo esta solución, pero no estoy seguro de que esta es la mejor:

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>) 
+0

Spot on - Nice one! –

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

(x \\ "_").filter(nodeHasValue(_,"test")) 
+0

Sugeriría la respuesta de maxme para el currying, pero mi respuesta para el uso de 'contiene' en lugar de un pliegue. ¡Combínalos! –

+0

Bueno, la respuesta de Daniel supera a ambos. Prestigio. –

3

fragmento de código en la pregunta no se trabaja con Scala 2,8 - debido a este comparasion

(_ == value)
necesita ser reemplazado con (_.text == value) o (_ == Text(value)) o cambio de tipo de valor de cadena a texto.

Y en la respuesta de Daniel (_.value == value) debe sustituirse por (_.value.text == value).

3

Las soluciones anteriores no me funcionaron porque todas buscan cualquier valor que coincida. Si desea buscar un determinado atributo con un valor, aquí está mi solución:

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

Y luego

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

Esto diferenciar entre class="test" y "notclass="test"

Cuestiones relacionadas