2016/12/12

How to use XML in scala


scala 內建了 scala.xml 可以處理 XML,以下我們簡單測試了一下 xml 的功能,目前看起來,標準函式庫的功能就足夠應用的需求了。


產生及使用 scala.xml


scala 可以直接偵測 < 這個符號,並以 xml 的方式處理,在程式中,直接把 xml指定給變數,就會直接產生 xml 物件。


scala> val xml = <store><item type="book">Novel</item><item type="pen">pencil</item></store>
xml: scala.xml.Elem = <store><item type="book">Novel</item><item type="pen">pencil</item></store>

產生 xml 物件後,可以用 text method,將所有欄位的 value 全部印出來


scala> xml.text
res0: String = Novelpencil

用 XPath 的語法,可以取得子節點的內容


scala> xml\"item"
res1: scala.xml.NodeSeq = NodeSeq(<item type="book">Novel</item>, <item type="pen">pencil</item>)

scala> (xml\"item").map(_.text).mkString(" ")
res2: String = Novel pencil

用 "@type" selector,可以取得所有 type 屬性的值


scala> (xml\"item").map(_\"@type")
res3: scala.collection.immutable.Seq[scala.xml.NodeSeq] = List(book, pen)

"@type" 只能取得目前這個節點的 children,如果要將所有出現同樣屬性的節點都取出來,就要用 "\" selector


scala> znodes\"z"
res5: scala.xml.NodeSeq = NodeSeq(<z x="1"/>)

scala> znodes\\"z"
res6: scala.xml.NodeSeq = NodeSeq(<z x="1"/>, <z x="2"/>, <z x="3"/>, <z x="4"/>)

跟剛剛一樣的方法,就可以找到屬性 x 的所有 values


scala> (znodes\\"z").map(_\"@x")
res12: scala.collection.immutable.Seq[scala.xml.NodeSeq] = List(1, 2, 3, 4)

如果是一個 xml string,可以用 scala.xml.XML.loadString 將 string 轉換為 xml。


scala> val xmlstring = """<store><item type="book">Novel</item><item type="pen">pencil</item></store>"""
xmlstring: String = <store><item type="book">Novel</item><item type="pen">pencil</item></store>

scala> val xmlfromstring = scala.xml.XML.loadString(xmlstring)
xml: scala.xml.Elem = <store><item type="book">Novel</item><item type="pen">pencil</item></store>

scala> xml == xmlfromstring
res13: Boolean = true

manual Serializing and Deserializing xml


首先定義 Stock class,在 toXml method 中,可以直接用 {symbol} 將 Stock 物件中的資料,產生到 xml 中,而 fromXml 就是用剛剛的 text 的方式取得 xml 裡面的資料放入 Stock 物件中。


case class Stock(var symbol: String, var businessName: String, var price: Double) {

  // convert Stock fields to XML
  def toXml = {
    <stock>
      <symbol>{symbol}</symbol>
      <businessName>{businessName}</businessName>
      <price>{price}</price>
    </stock>
  }

  override def toString =
    s"symbol: $symbol, businessName: $businessName, price: $price"

}

object Stock {

  // convert XML to a Stock
  def fromXml(node: scala.xml.Node):Stock = {
    val symbol = (node \ "symbol").text
    val businessName = (node \ "businessName").text
    val price = (node \ "price").text.toDouble
    new Stock(symbol, businessName, price)
  }
}

用個簡單的 TestXml 測試 toXml 及 fromXml


object TestXml extends App {

  // convert a Stock to its XML representation
  val aapl = new Stock("AAPL", "Apple", 600d)
  println(aapl.toXml)

  // convert an XML representation to a Stock
  val googXml = <stock>
    <symbol>GOOG</symbol>
    <businessName>Google</businessName>
    <price>620.00</price>
  </stock>
  val goog = Stock.fromXml(googXml)
  println(goog)
}

執行結果如下


<stock>
      <symbol>AAPL</symbol>
      <businessName>Apple</businessName>
      <price>600.0</price>
    </stock>

symbol: GOOG, businessName: Google, price: 620.0

References


Basic XML processing with Scala


Practical Scala – processing XML


Serializing and deserializing XML in Scala


Generating dynamic XML from Scala source code (like a template)