Ontem eu precisei extrair alguns dados de um xml, e acho que as lições aprendidas merecem ficar registradas, vai que mais alguem nesse mundão precisa fazer algo assim.
<request service="Service 1">
<extra>
<entry key="key1">value1
<entry key="key2">value2
</extra>
</request>
O objetivo era extrair o nome do service como uma string e as entries como um map de key values.
O servico como string é bem facil, considerando que o xml esta em uma variavel serviceXml, teriamos:
val serviceName = (serviceXml\"@service").text
O que isso faz é basicamente pegar o attributo service do nó em questão e com esse dado pegar o nó como text.
A segunda parte é um pouco mais complexa então vamos por parte, primeiro pegamos as entries:
val entries = serviceXml\"extra"\\"entry"
Esse codigo executa basicamente um XPath extraindo do xml o nó "extra" e deste a lista de nós "entry"
Então, para cada entry extraimos uma tupla com o valor da key e o value:
val entriesTuple = for (entry <- entries) yield ((entry\"@key").text, entry.text)
Aqui usamos um for expression para percorrer a lista entries e retornar (usando yield) uma tupla que contem o valor da key e o inner text da entry
E por fim criar um Map com esses dados:
val mapResult = Map(entriesTuple: _*)
Por fim, como construir um Map apartir de uma lista, ou nesse caso de um generator ?
Maps podem ser contruidos usando de um numero variavel de tuplas usando algo como Map(tupla1, tupla2, tuplaN).
Se tentarmos usar Map(entriesTuple) o compilador vai reclamar falando que esse metodo nao pode ser executado com uma Seq[(String, String)], ou seja, uma sequencia de tuplas contendo duas Strings.
Temos que dar um jeito de passar essa Seq como um numero variavel de argumentos, e os desenvolvedores de Scala nos deram uma forma de fazer isso, quando temos que passar uma lista para um método que espera um número variavel de argumentos podemos fazer assim:
Map(entriesTuples: _*)
Essa anotação diz ao compilador para expandir os parametros
Juntando tudo temos:
val mapResult = Map((for (entry <- serviceXml\"extra"\\"entry") yield ((entry\"@key").text, entry.text)): _*)
Muita explicação pra pouco código, assim é Scala
Cheers