2012-07-09 21 views
5

ho questo codice:Groovy MarkupBuilder nome conflitto

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 questo xml:

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

Si noti che il tag "identità" manca ... Ho provato di tutto nella mondo per far apparire quel nodo. Mi sto strappando i capelli!

Grazie in anticipo.

risposta

11

Ci potrebbe essere un modo migliore, ma un trucco è quello di chiamare direttamente 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(); 
} 

Questo è effettivamente ciò che Groovy sta facendo dietro le quinte. Non sono riuscito a far funzionare delegate.identity o owner.identity, che sono i soliti trucchi.


Edit: ho capito cosa sta succedendo.

Groovy adds a method con una firma di identity(Closure c) ad ogni oggetto.

Ciò significa che quando si è tentato di richiamare in modo dinamico l'elemento identity sul costruttore di XML, passando in un unico argomento di chiusura, si stava chiamando il metodo identity(), che è come chiamare delegate({...}) sulla chiusura esterna.

Utilizzando il trucco invokeMethod, Groovy ignora il Meta Object Protocol e tratta il metodo come metodo dinamico, anche se il metodo identity esiste già su MetaObject.

Sapendo questo, possiamo mettere insieme una soluzione migliore, più leggibile. Tutto ciò che dobbiamo fare è cambiare la firma del metodo, in questo modo:

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

Questo è molto più leggibile, è chiaro l'intento, e il commento dovrebbe (si spera) impedire a chiunque di rimuovere il "superfluo" vuoto carta geografica.

+0

Ha funzionato, ma puoi spiegarlo? Cosa è delegato e perché delegate.identity è diverso da delegate.invokeMethod ('identity')? –

+0

L'ho capito, aggiornerò la mia risposta. – OverZealous

+0

FYI: Ho rintracciato questo usando la GroovyConsole per ispezionare l'oggetto XML. Questo fammi sapere che il metodo 'identity' esisteva già con un singolo' Closure' come argomento. – OverZealous

Problemi correlati