Scala の XML リテラル入門
XML を読む
val xml = <root> <items> <item isTarget="false" > <name>foo</name> <age>19</age> </item> <item isTarget="true" > <name>bar</name> <age>24</age> </item> </items> </root>
XML オブジェクトにはエレメントを探索できる「\」「\\」といったメソッドがあります。
「\」は直下のエレメントの指定、「\\」は間のエレメントを飛ばして探索ができるメソッドです。
なお、はてなの仕様でバックスラッシュはコード例の中では円マークになっています。
指定するとき、ルートエレメントは省略します。
// val items = xml \ "root" \ "items" \ "item" // 見つからない(空のNodeSeq) val items = xml \ "items" \ "item"
「\\」の場合は欲しいエレメントのタグ名を指定するだけです。
val items = xml \\ "item"
items は scala.xml.NodeSeq 型です。コレクション型として処理ができます。
items foreach { item => { val name = item \ "name" val age = item \ "age" val isTarget = item \ "@isTarget" println("name:" + name.text + ",age:" + age.text + ",isTarget:" + isTarget) } }
XML を書く
{}で囲んだ部分には Scala のコードを埋め込む事ができます。
case class Item(val name: String, val age: Int, val isTarget: Boolean) val item1 = Item("foo", 19, false) val item2 = Item("bar", 24, true) val xml = <root> <items> <item isTarget={item1.isTarget.toString} > <name>{item1.name}</name> <age>{item1.age}</age> </item> <item isTarget={item2.isTarget.toString} > <name>{item2.name}</name> <age>{item2.age}</age> </item> </items> </root>
以下のように for 式などの処理を埋め込む事もできます。REPL で動作するためには以下のように {} 部分は一行で書く必要があります。
case class Item(val name: String, val age: Int, val isTarget: Boolean) val items = List(Item("foo", 19, false), Item("bar", 24, true)) val xml = <root> <items> { items map { i => <item isTarget={i.isTarget.toString} ><name>{i.name}</name><age>{i.age}</age></item> } } </items> </root> val xml = <root> <items> { for( i <- items) yield <item isTarget={i.isTarget.toString} ><name>{i.name}</name><age>{i.age}</age></item> } </items> </root>
シリアライズ/デシリアライズ
abstract class Item { val name: String val age: Int val isTarget: Boolean def toXML = <item isTarget={isTarget.toString} ><name>{name}</name><age>{age}</age></item> } // シリアライズ val item = new Item { val name = "foo" val age = 19 val isTarget = false } val xml = item.toXML // <item isTarget="false"><name>foo</name><age>19</age></item> object Item { def fromXML(node: scala.xml.Node): Item = new Item { val name = (node \ "name").text val age = (node \ "age").text.toInt val isTarget = (node \ "@isTarget").text.toBoolean } } // デシリアライズ val item = Item.fromXML(<item isTarget="false"><name>foo</name><age>19</age></item>)
ファイル出力する
import scala.xml.XML val xml = <root><items><item isTarget="false" ><name>foo</name><age>19</age></item><item isTarget="true" ><name>bar</name><age>24</age></item></items></root> XML.saveFull("items.xml", xml, "UTF-8", true, null) // <?xml version='1.0' encoding='UTF-8'?> // <root><items><item isTarget="false"><name>foo</name><age>19</age></item><item isTarget="true"><name>bar</name><age>24</age></item></items></root>
ロードする
文字列をパースして XML オブジェクトをロードします。
import scala.xml.XML XML.loadString("""<root><items><item isTarget="false" ><name>foo</name><age>19</age></item><item isTarget="true" ><name>bar</name><age>24</age></item></items></root>""")
ファイルから XML オブジェクトをロードします。
import scala.xml.XML XML.loadFile("items.xml")
パターンマッチ
val item = <item isTarget="false" ><name>foo</name><age>19</age></item> item match { case <item><name>{name}</name><age>{age}</age></item> => println(name) // foo case _ => }
任意の文字列として一部を扱ってより簡潔に書く事ができます。「_*」は任意の文字列を示し、以下の例ではマッチした部分を"unused"に代入しています。
item match { case <item><name>{name}</name>{unused @ _*}</item> => println(name) // foo case _ => }
もちろん使わない部分をまとめるためだけではなく、以下のように「_*」を使って抽出処理を書く事ができます。
item match { case <item>{elements @ _*}</item> => { elements foreach { el => println(el.text) // "foo" "19" } } case _ => } val items = <items> <item isTarget="false" ><name>foo</name><age>19</age></item> <item isTarget="true" ><name>bar</name><age>24</age></item> </items> for ( <item>{elements @ _*}</item> <- items \ "item" ) { elements foreach { el => println(el.text) // "foo" "19" "bar" "24" } } // for ( elements @ <item>{_*}</item> <- items \ "item" ) { // と書いた場合はelementsとして<item isTarget="false" ><name>foo</name><age>19</age></item>がとれます