Scalaで再帰的な構造のXMLを生成する

もう誰かやっている気がするけども。
元ネタ
GroovyのMarkupBuilderで再起的な構造のXMLを生成する - No Programming, No Life
GroovyでKey、ValueをXMLにクールに出力する-keyValueXml.groovy- - Togetter
C# で Key、Value な コレクションを XML にしてみた - お だ のスペース

ポリモーフィズム

本体

テスト

Scalaで書くからには型安全にしたく、Valueクラスをみんな継承してやってます。
あと属性を指定できるようにしました。


Elemのコンストラクタさんは第一引数(prefix)に空文字列を渡すとnullにしろ(nullかよ)とか、第四引数(scope)にnullを渡すとTopScopeにしろとか言ってくれるのは有り難いんだけど、第三引数(attributes)にnull渡しても動作する癖にPrettyPrinterでformatしようとするとぬるぽが出てしばらく詰まった。
scala.xml.Nullを渡さないといけないんですね、最初からソース読めよってことでした。


その渡すものを作ってる部分、

def meta = ((Null: MetaData) /: attributes){ (meta, attr) =>
  new UnprefixedAttribute(attr._1, attr._2, meta)
}

みんな大好きfoldLeftさん、別名/:。初期値を(Null: MetaData)として型を指定していますが、これは Null /: attributes だと戻り値の型がscala.xml.NullってことになりUnprefixedAttributeはダメって怒られるからです。共通の親であるMetaDataなら安心。


以下は書いててちょっと不満に思った点なんですが、
case classのサブなコンストラクタを使うときにnewって書かなきゃいけないのが面倒。どうにかなんないのかなこれ。ほんとは TupleN => KeyValue なimplicitを書きたいところだったんだけど、暗黙型変換は1回しかやらないってルールなのでそれはしょうがないにしても。

あと、XMLリテラルが間の改行とかインデントまで考慮してくれちゃうのはありがた迷惑な気が致します。StringのstripMarginみたいなノリで空白文字だけのテキストを無視したXMLを返してくれるモノはないのかしらん。
PrettyPrinterが正にそれをやってるじゃないかと思ったところ、

case Text(s) if s.trim == "" => ;

これで無視してStringBuilderに渡してないってだけですね。でも同じようにして出来るのかな。

パターンマッチング版

本体

テストはポリモーフィズム版とほぼ一緒なので省略。
どっちのやり方がいいかって議論は何かで読んだ気がするけど何だっけ。個人的に好きなのはポリモーフィズム版なんだけど、こちらの方がよりScalaらしいのかな。
親に操作を追加すると子をすべて編集しなきゃいけない vs データの種類を増やすと操作をすべて編集しなきゃいけない(けど全部一箇所にまとまってるよね)、でケースバイケースという話だったかな。