2012-10-26 27 views
3

我正在使用Groovy MarkupBuilder將地圖轉換爲XML。該地圖可以包含簡單的鍵/值對,其他地圖或地圖列表。我從代碼here捎帶。如何使用Groovy的MarkupBuilder呈現列表

import groovy.xml.MarkupBuilder 

def map = [ 
    key1:'value1', 
    key2:'value2', 
    nestedMap : [ 
     key1:'bar1', 
     key2:'bar2' 
    ], 
    select : [ 
     [option:'foo1'], 
     [option:'foo2'] 
    ] 
] 

Closure renderMap(Map map){ 
    return { 
     for (entry in map){ 
      switch(entry.value.getClass()){ 
       case Map : 
        "${entry.key}" renderMap(entry.value) 
       break 
       case List: 
        entry.value.collect { listEntry -> 
         "${entry.key}" renderMap(listEntry) 
        } 
        break 
       default : 
        "${entry.key}"("${entry.value}") 
       break 
      } 
     } 
    } 
} 

StringWriter writer = new StringWriter() 
new MarkupBuilder(writer).root renderMap(map) 

println writer.toString() 

這部分我擔心打印出:

<select> 
    <option>foo1</option> 
    </select> 
    <select> 
    <option>foo2</option> 
    </select> 

不過,我想知道如果有一種方式來獲得選擇封裝兩個選項,如下所示:

<select> 
    <option>foo1</option> 
    <option>foo2</option> 
    </select> 

我試着玩弄鑰匙的位置,但無濟於事。我是否做過這一切都錯了,還是我不應該使用生成器?

回答

6

我認爲這會做你想做的。前兩個重載使用映射或集合,並返回可以傳遞給封閉元素的構建器方法的構造閉包,以將地圖或集合的內容添加到構建器。

第三個是後備,只是返回它的參數,以便它們可以傳遞給構建器方法。這將處理字符串,但如果需要,也可以將它傳遞給閉包。我用你提供的地圖中的第二個option元素替換爲例子。

ComposedClosure被添加到Groovy 1.8中,所以這在早期版本中不起作用。

import groovy.xml.MarkupBuilder 

Closure buildxml(final Map map) 
{ 
    final compose = { f, tag, content -> f >> { "$tag"(buildxml(content)) } } 
    return map.inject(Closure.IDENTITY, compose) 
} 

Closure buildxml(final Collection col) 
{ 
    final compose = { f, content -> f >> buildxml(content) } 
    return col.inject(Closure.IDENTITY, compose) 
} 

def buildxml(final content) 
{ 
    return content 
} 

def map = [ 
    key1:'value1', 
    key2:'value2', 
    nestedMap : [ 
     key1:'bar1', 
     key2:'bar2' 
    ], 
    select : [ 
     [option:'foo1'], 
     { option('foo2') }, 
    ], 
] 

final writer = new StringWriter() 
final builder = new MarkupBuilder(writer) 

builder.root buildxml(map) 

assert writer as String == '''\ 
<root> 
    <key1>value1</key1> 
    <key2>value2</key2> 
    <nestedMap> 
    <key1>bar1</key1> 
    <key2>bar2</key2> 
    </nestedMap> 
    <select> 
    <option>foo1</option> 
    <option>foo2</option> 
    </select> 
</root>'''.stripIndent() 
+1

因爲它不是3AM,所以能夠大幅簡化它。但Map和Collection的重載仍然會讓我感到一些困擾。他們看起來多餘,但我想不出有什麼辦法可以將它們組合起來,這比只使用兩種幾乎相同的方法更清潔。 –

+0

非常乾淨優雅的答案,謝謝。 – Igor

1

確實

case List: 
    "${entry.key}" entry.value.collect { 
     renderMap it 
    } 
    break 

讓你隨時隨地?不是在電腦上查看atm,但感覺正確嗎?

+0

我已經嘗試過,沒有運氣。它打印出:''。不過謝謝。 – Igor

+0

啊,是啊。你可以用'renderMap(it)()'調用閉包,但那可能會破壞更深的嵌套結構。我會想一想 –

相關問題