【问题标题】:How to render a List with Groovy's MarkupBuilder如何使用 Groovy 的 MarkupBuilder 渲染列表
【发布时间】:2012-10-26 22:53:30
【问题描述】:

我正在使用 Groovy MarkupBuilder 将 Map 转换为 XML。此 Map 可以包含简单的键/值对、其他 Map 或 Map 列表。我从代码 here 捎带。

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 封装两个选项,如下所示:

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

我尝试过调整键的位置,但无济于事。我是不是搞错了,还是我不应该使用构建器?

【问题讨论】:

    标签: xml groovy map


    【解决方案1】:

    我认为这会满足您的需求。前两个重载采用映射或集合,并返回一个组合闭包,该闭包可传递给封闭元素的 builder 方法,以将映射或集合的内容添加到构建器。

    第三个是回退,只返回它的参数,以便它们可以传递给构建器方法。这会处理字符串,但如果需要,您也可以将其传递给闭包。我替换了您提供的地图中的第二个 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()
    

    【讨论】:

    • 现在不是凌晨 3 点,因此能够大大简化这一过程。 Map 和 Collection 的重载仍然让我有些烦恼。它们似乎是多余的,但我想不出一种比仅使用两种几乎相同的方法更干净的方法来组合它们。
    【解决方案2】:

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

    带你去任何地方?虽然不是在电脑上检查atm,但感觉对吗?

    【讨论】:

    • 我实际上已经尝试过了,但没有运气。它打印出:&lt;select&gt;[Script1$_renderMap_closure1@1075c5, Script1$_renderMap_closure1@a3e8f5]&lt;/select&gt;。不过还是谢谢。
    • 啊,是的。你可以用renderMap( it )() 调用闭包,但这可能会破坏更深的嵌套结构。我会考虑的
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-10
    • 2012-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多