How to render a List with Groovy's MarkupBuilder

三世轮回 提交于 2020-01-23 10:47:07

问题


I am converting a Map to XML using the Groovy MarkupBuilder. This Map can contain simple key/value pairs, other Maps, or Lists of Maps. I am piggybacking from the code 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()

This part I'm concerned about prints out:

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

However, I am wondering if there is a way to get the select to encapsulate both of the options, like so:

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

I've tried playing around with the placement of the key, but to no avail. Am I going about this all wrong, or should I not be using the builder?


回答1:


I think this will do what you want. The first two overloads take a map or a collection, and return a composed closure that can be passed to the builder method of the enclosing element to add the contents of the map or collection to the builder.

The third is a fallback, and just returns its arguments so they can be passed to the builder method. This handles the strings, but you could also pass it a closure if you want. I replaced the second option element in the map you provided as an example of that.

ComposedClosure was added in Groovy 1.8, so this won't work in earlier versions.

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()



回答2:


does

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

get you anywhere? not at a computer to check atm though, but it feels right?



来源:https://stackoverflow.com/questions/13095486/how-to-render-a-list-with-groovys-markupbuilder

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!