MarkupBuilderで要素の作成を別メソッドにする方法がわからない。
GroovyでXMLを出力するにはMarkupBuilderを使えば簡単らしいのでやってみた。
<root> <id>1</id> <user> <name>yamada</name> <car> <name>car1</name> </car> </user> <company> <name>kaisha</name> <car> <name>car2</name> </car> </company> </root>
このXMLを出力したい場合には以下のコードで出力可能。
import groovy.xml.MarkupBuilder new MarkupBuilder().root(){ id(1) user { name("yamada") car{ name("car1") } } company { name("kaisha") car { name("car2") } } }
さて、ここからが問題。
car要素が複数の出てるのでをまとめて記述したい場合のスマートな書き方がわからない。
とりあえずまとめるだけならroot内にクロージャーを定義すれば可能。
参考↓
http://groovy.my-notebook.net/435028f2-6101-496a-bc42-2521361faceb.html
import groovy.xml.MarkupBuilder new MarkupBuilder().root(){ // car要素を作成 def c = {n -> car{ name(n) } } id(1) user { name("yamada") c("car1") } company { name("kaisha") c("car2") } }
少しはマシになったけど、外部に切り出せてるわけではないので微妙。
こんな感じで書きたい。
// carを作成 def c = {n -> car{ name(n) } } // userを作成 def u = { user { name("yamada") c("car1") } } // companyを作成 def co = { company { name("kaisha") c("car2") } } // 出力 builder.root(){ id(1) u() c() }
MarkupBuilderのクロージャー内で呼ばれる関数が何を返せばいいのかわかればよい?
そもそも、MarkupBuilderが「このような記述でXMLが出力されるのか」を理解する必要があるような気がする。
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
とか思って、
つまり、MarkupBuilderに渡されたクロージャー内で定義されていないメソッドが呼ばれるとmethodMissingが呼ばれて、
メソッド名として記述されている値が要素として出力されると。
では、この場合はどういう風に処理が走ってる?
new MarkupBuilder().root(){ def c = {n -> car{ name(n) } } user { c("car1") } }
最初のクロージャー内で「c」という変数名で定義されたクロージャーがあるのは特に問題なし。
user {
c("car1")
}
というのは
user({c("car1")})
という事になって、userメソッドが呼び出される。
しかし、userメソッドは当然定義されていないのでmethodMissing()にuserという文字列と「{c("car1")}」というクロージャーが渡されると。
ならば、MarkupBuilder外にメソッド作成してクロージャー返せばいいのでは?
と、思ったけど出力されない。
が、例外になるわけでもない。
import groovy.xml.MarkupBuilder def c = {n -> return { car{ name(n) } } } new MarkupBuilder().root(){ user { c("car1") } } /* <root> <user /> </root> */
これは、メソッドからクロージャーが返ってくるがこのクロージャーが実行されていないため。
んじゃ、クロージャー実行すればいいやん。
って事で返ってきたクロージャー実行するとMissingMethodExceptionが発生。
import groovy.xml.MarkupBuilder def c = {n -> return { car{ name(n) } } } new MarkupBuilder().root(){ user { c("car1")() } } // groovy.lang.MissingMethodException: No signature of method: ConsoleScript11.car() <略>
MarkupBuilderの外だからmethodMissingメソッドが定義されていない。
だからMissingMethodExceptionが発生する。
じゃぁ、methodMissingで行なっている処理を行えばいい。
って思ったんですが、出力してるんですよね。
っとなると、手詰まり??
何か方法ありそうだけど。。。
追記(2012/12/23)
id:kiy0taka さんからコメント頂きました。
外部で定義するクロージャーでdelegateを使用すれば実現できるようです。
import groovy.xml.MarkupBuilder def xml = new MarkupBuilder() def c = {n -> car{ name(n) } } c.delegate = xml xml.root(){ id(1) user { name("yamada") c("car1") } company { name("kaisha") c("car2") } }
delegateというとクロージャー内でthis的に使うという事しかわからなかったので調べたら、↓という事らしい。
owner : そのクロージャを含むオブジェクト (this またはそのクロージャを囲んでいるクロージャ)
http://groovy.codehaus.org/Japanese+Closures
delegate : デフォルトでは owner と同じだが変更可能。例: builder や ExpandoMetaClass
つまり、外部で定義したクロージャーをMarkupBuilder内のクロージャーとして扱っているという事ですかね。
delegateってこうやって使うのね!