Найдите все узлы, у которых есть атрибут, который соответствует определенному значению с помощью scala

Я увидел следующий пример Nabble, целью которого было вернуть все узлы, содержащие атрибут с идентификатором 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")) 

Но это не сработает. Я знаю, что могу использовать сопоставление с образцом, но просто хотел посмотреть, могу ли я сделать магию с фильтрацией.

Приветствия - Эд

Ответ 1

Во-первых, 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" , который заканчивается довольно уродливым.

Ответ 2

Фрагмент кода в вопросе не работает с Scala 2.8 - из-за этого сравнения

(_ == value)
Нужно заменить на (_.text == value) или (_ == Text(value)) или изменить тип значения из строки в текст.

И в ответе Даниэля (_.value == value) необходимо заменить на (_.value.text == value).

Ответ 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"

Ответ 4

Я новичок в 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>)

Ответ 5

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

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