Scala で XML を編集する 21:45

Posted at 2010/01/19 21:45, Modified at 2010/01/20 22:45

Scala で XML を読んで、そこからなにかを抜き出すには、パターンマッチや scala.xml.NodeSeq の \, \\ といったメソッドを使うだろう。また、新しい XML 文章を作るのなら XML リテラルでだらだら書ける。では、既存の XML 文章をちょっと変えるには、具体的には

  • link rel="self" の href を変えて
  • link rel="hub" な要素を追加

したいときにどうするんだろう、というのでややつまった。

最初は \\ で対象ノードをみつけて消し足ししようかと思っていたのだけど scala.xml のあたりは全部 immutable なのでそうはいかない。結局、Scala, XML and GAE というスライドの12ページの

def add(p: Node, newEntry: Node ): Node = {
  p match {
  case <div>{ ch @ _@ }</div> =>
    <div>{ ch }{ newEntry }</div>
}

をまねて、こう落ち着いた。

import scala.xml._
import org.scalatest.FunSuite

class XMLModifySuite extends FunSuite {
  def process(seq: Node): Option[Node] = {
    seq match {
      case link @ <link/> => {
        if (link.attributes("rel") == "self") {
          Some(Group(List(<link rel="self" href="http://example.org/" />,
                          <link rel="hub" href="http://example.org/hub" />)))
        } else if (link.attributes("rel") == "hub") {
          None
        } else {
          Some(link)
        }
      }
      case e: Elem => {
        val child = e.child.flatMap(c => process(c) match {
          case Some(v) => v
          case None    => Nil
        })
        Some(Elem(e.prefix, e.label, e.attributes, e.scope, child : _*))
      }
      case _ @ n => {
        Some(n)
      }
    }
  }

  test("Remove hub") {
    assert(process(<link rel="hub" href="http://example.org/hub"/>) === None)
    assert(process(<feed><link rel="hub" href="http://example.org/hub"/></feed>).get ===
           <feed></feed>)
  }

  test("Replace self and remove hub") {
    assert(process(<feed><link rel="self" href="http://example.com"/></feed>).get ===
           <feed><link rel="self" href="http://example.org/"/><link rel="hub" href="http://example.org/hub"/></feed>)
  }

  test("Else") {
    assert(process(<feed><title>Hello</title></feed>).get ===
           <feed><title>Hello</title></feed>)
  }
}

「マッチ」で編集するのは、気持ちとしては正規表現みたいな感じ。といっても XML は再帰的な構造をもってるので、ややごちゃっとするなあ。

追記

match が link と Elem しかうけてなかったので

      case _ @ n => {
        Some(n)
      }

と、テストを足しました。

0 comments
riddle for guest comment authorization:
Where is the capital city of Japan? ...

blog.8-p.info加藤和良 の個人的なブログで、プログラミングのはなしが多めです。