2012-10-26 7 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)()'을 사용하여 클로저를 호출 할 수는 있지만 더 깊게 중첩 된 구조가 손상 될 수 있습니다. 나는 생각할 것이다 –

관련 문제