2014年8月8日金曜日

Programming in Scala, First Edition: Chapter 26

26. Working with XML
  XMLの半構造化データについての一般的な議論
  scalaでXMLをどうやって扱うかの重要な部分の概要説明
    XMLリテラルを用いたノードの作成。
    XMLファイルの保存と読み込み。
    queryとpattern matchingを使ったノードの分離。

26.1 Semi-structured data
  XMLは半構造化された形式を取っている。
  ツリー構造になっていてplain textよりは構造化されている。
  プログラム言語のオブジェクトよりは構造化されていない。
    タグ間はフリーフォーマット。
    型システムもない。
  半構造化はプログラムでデータをシリアライズする時に非常に有用
    ネットワーク経由で送信したり、ディスクに保存する場合。
    構造化されているデータ全てをバイナリにする代わりに使える。
      まずは半構造化フォーマットに変換する。
      その後に半構造化フォーマットに元々準備されているライブラリでバイナリにする。
  半構造化フォーマットは色々あるがInternetではXMLが一番一般的。
    ほとんどのOSのほとんどの言語にXMLライブラリが存在する。
    ネットワーク外部性により益々使われる様になっている。
    Internet経由でのやり取りをする場合はXMLを避けて通れない。
  scalaでもXMLの特別なサポートを行っている。
    本章で標準的なメソッドやpattern matchingによる処理を紹介。
    さらに良く使われるidiomも紹介する。

26.2 XML overview
  tagとtextで構成される。
    textは任意
    tagは<tag></tag>
  tagはstart<tag>とend</tag>があって対応付ける必要あり。省略はNG。
    <tag/>: startとendを合わせた表現
    <tag attr="3">: start tagには属性をつけられる

26.3 XML literals
  scalaではどこにでもXMLのリテラルを記載する事が出来る
  単にstart tagで書き始めればend tagまでXMLリテラルと認識される。
    scala> <a>
             This is some XML.
             Here is a tag: <atag/>
           </a>
    res0: scala.xml.Elem = ...
  Type Elem: XML element
  Class Node: 全てのXML node classの上位の抽象クラス
  Class Text: testのみを保持しているXMLのnode。<a>stuff</a>の中の"stuff"
  Class NodeSeq: XML nodeのsequence
    XMLのライブラリはNodeSeqを処理する様に作られている
    Nodeは単一NodeのみのNodeSeqとして定義されている。
      NodeSeqに対する処理は単一Nodeに対しても行える。
  XMLの内部にも{}を使ってscalaコードを埋め込める。
    scala> <a> {"hello"+", world"} </a>
    res1: scala.xml.Elem = <a> hello, world </a>
    任意のscalaコードを書けるので、その中に更にXMLを埋め込むことも可能。
    scalaコードの評価結果が埋め込まれてXMLになる。
      空のXMLを表現したい場合はxml.NodeSeq.Emptyを使う。
    評価結果がXML nodeでない場合は、stringに変換されてtextとして埋め込まれる。
    text中の<、>、&はURL encodeされる。
    XMLのtagと同様のstringをつあってもtextとして認識される。
      tagとして認識させたい場合はXMLリテラルかXMLのtype(型)を使う。

26.4 Serialization
  XMLリテラルとbraceによるscalaコードの埋め込みでシリアル化が出来る。
  下記がシリアル化(XML化)の例
    abstract class C {
      val s: String;
      val i: Int;
      def toXML =
       <c>
         <s>{s}</s>
         <i>{i}</i>
       </c>
    }
   下記の様に実行できる。
     scala> val c = new C {
              val s = "hoge"
              val i = 10
            }
     scala> c.toXML
     abstractクラスでもanonymous classを使ってnewできる。
   {}をtextとして使いたい場合は{{、}}を使う。

26.5 Taking XML apart
  多数あるXMLライブラリのメソッドの中で特に良く使うのは3つ
    XMLを分解するためのメソッド。
    XPath言語に基づいている。
    外部ツールを呼び出すことなく直接scalaから使える。
  Extracting text
    textメソッドを使うと全要素のtext部分が一つのstringに連結されて抽出される。
      scala> <a>Sounds <tag/> good</a>.text
      res8: String = Sounds  good
    URL encodeされている場合は自動的にdecodeされる。
  Extracting sub-elements
    \"tag"メソッドを使うとタグが"tag"の子要素のNodeSeqが抽出される。
      scala> <a><b><c>hello</c></b></a> \"b"
      res10: scala.xml.NodeSeq = <b><c>hello</c></b>
    \\"tag"メソッドを使うとツリー全体を検索して(deep search)要素を取り出す。
      topレベル要素も含めて全体が検索される。
    \、\\はXPathの/、//に対応している。
      scalaでは/、//を別の意味で使うので\、\\とした。
  Extracting attributes
    \"@attr"、\\"@attr"で属性値のNodeSeqが取り出せる。

26.6 Deserialization
  textメソッドを使えばシリアル化してXMLにしたオブジェクトを逆シリアル化出来る。
    def fromXML(node: scala.xml.Node): C =
      new C {
        val s = (node \"s").text
        val i = (node \"i").text.toInt
      }
  ** 実際に定義する時はclass Cのcompanion objectに定義すると良さそう。

26.7 Loading and saving
  toStringを使うだけでもXMLをStringにすることは出来る。
  ライブラリの関数を使った方がより良い。
    encode情報等のdirectiveをつける事が出来るため
  XML.saveFull(ファイル名, XMLノード, エンコード, エンコードフラグ, ドキュメントタイプ)
    scala.xml.XML.saveFull("therm1.xml", node, "UTF-8", true, null)
    エンコードフラグはencode declarationをつけるかどうか。
    ドキュメントタイプは説明省略。nullで未定義にしておくので良い。
  XML.loadFile(ファイル名)
    scala> val loadnode = xml.XML.loadFile("therm1.xml")

26.8 Pattern matching on XML
  XML pattern
    XMLリテラルと同様の形式。
    {}の内側はscalaのpattern matchingのパターンになる。
      変数束縛、型テスト、ワイルドカード(_、_*)
  (node :scala.xml.Node) match { case <a>{contents}</a> => ... }
    「single sub-node」のみmatchする。
    子要素が一つの場合。
      <a>hoge</a>にはmatchする。
    孫要素があってもOK.
      <a><b>hoge</b></a>にはmatchする。
    子要素が複数ならmatchしない。
      <a><b>hoge</b><b>fuga</b></a>にはmatchしない。
  (node :scala.xml.Node) match { case <a>{contents @ _*}</a> => contents }
    子要素が複数の場合は_*を使ってSeqとしてmatchさせる。
      <a><b>hoge</b><b>fuga</b></a>にはmatchする。
      Seq[scala.xml.Node] = ArrayBuffer(<b>hoge</b>, <b>fuga</b>)
  空白文字
    XMLリテラルを使うときに空白文字に注意する。
    タグ間の改行やタブ等もXMLのtextと要素として認識されるので注意が必要。
    下記だと<a>の子要素として<b>と<c>と</a>の前の改行+空白の3要素も存在する。
      <a>
        <b>hoge</b>
        <c>fuga</c>
      </a>

26.9 Conclusion
  scalaではXMLの特別なサポートとしてXMLリテラルがプログラムの何処でも使える。
  他にも沢山の拡張、ライブラリ、ツールがある。
    scala向けにカスタマイズされたもの
    Java向けだがsalaでもつかえるもの
    言語非依存のもの

0 件のコメント:

コメントを投稿