2012-07-09 14 views
5

tengo este código:maravilloso MarkupBuilder conflicto de nombres

String buildCatalog(Catalog catalog) { 
    def writer = new StringWriter() 
    def xml = new MarkupBuilder(writer) 
    xml.catalog(xmlns:'http://www.sybrium.com/XMLSchema/NodeCatalog') { 
     'identity'() { 
      groupId(catalog.groupId) 
      artifactId(catalog.artifactId) 
      version(catalog.version) 
     } 
    } 

    return writer.toString(); 
} 

Produce este xml:

<catalog xmlns='http://www.sybrium.com/XMLSchema/NodeCatalog'> 
    <groupId>sample.group</groupId> 
    <artifactId>sample-artifact</artifactId> 
    <version>1.0.0</version> 
</catalog> 

en cuenta que la etiqueta de "identidad" se ha omitido ... He intentado todo en el mundo para que aparezca ese nodo. ¡Me estoy arrancando el pelo!

Gracias de antemano.

Respuesta

11

Puede haber una mejor manera, pero un truco es llamar directamente invokeMethod:

String buildCatalog(Catalog catalog) { 
    def writer = new StringWriter() 
    def xml = new MarkupBuilder(writer) 
    xml.catalog(xmlns:'http://www.sybrium.com/XMLSchema/NodeCatalog') { 
     delegate.invokeMethod('identity', [{ 
      groupId(catalog.groupId) 
      artifactId(catalog.artifactId) 
      version(catalog.version) 
     }]) 
    } 

    return writer.toString(); 
} 

Esto es efectivamente lo maravilloso está haciendo detrás de las escenas. No pude hacer funcionar delegate.identity o owner.identity, que son los trucos habituales.


Editar: me di cuenta de lo que está pasando.

Groovy adds a method con una firma de identity(Closure c) para cada objeto.

Esto significa que cuando se trató de invocar dinámicamente el elemento identity en el constructor de XML, mientras que pasa en un solo argumento de cierre, se llama al método identity(), que es como decir que delegate({...}) en el cierre exterior.

Usando las fuerzas invokeMethod truco maravilloso para eludir el Protocolo Meta Object y tratar el método como un método dinámico, a pesar de que el método identity ya existe en el meta objeto.

Sabiendo esto, podemos armar una solución mejor y más legible. Todo lo que tenemos que hacer es cambiar la firma del método, así:

String buildCatalog(Catalog catalog) { 
    def writer = new StringWriter() 
    def xml = new MarkupBuilder(writer) 
    xml.catalog(xmlns:'http://www.sybrium.com/XMLSchema/NodeCatalog') { 
     // NOTE: LEAVE the empty map here to prevent calling the identity method! 
     identity([:]) { 
      groupId(catalog.groupId) 
      artifactId(catalog.artifactId) 
      version(catalog.version) 
     } 
    } 

    return writer.toString(); 
} 

Esto es mucho más fácil de leer, es más clara la intención, y el comentario debe (con suerte) evitar que cualquier persona la eliminación de la "innecesaria" vacía mapa.

+0

Eso funcionó, pero ¿puedes explicar esto? ¿Qué es delegado y por qué es delegate.identity diferente de delegate.invokeMethod ('identity')? –

+0

Lo descubrí, actualizaré mi respuesta. – OverZealous

+0

FYI: rastreé esto utilizando GroovyConsole para inspeccionar el objeto XML. Esto me permite saber que el método 'identity' ya existía con un solo' Closure' como argumento. – OverZealous