Groovy MarkupBuilder name conflict

前端 未结 1 1209
傲寒
傲寒 2020-12-11 19:13

I have this code:

String buildCatalog(Catalog catalog) {
    def writer = new StringWriter()
    def xml = new MarkupBuilder(writer)
    xml.catalog(xmlns:\'         


        
相关标签:
1条回答
  • 2020-12-11 19:16

    There might be a better way, but one trick is to call invokeMethod directly:

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

    This is effectively what Groovy is doing behind the scenes. I couldn't get delegate.identity or owner.identity to work, which are the usual tricks.


    Edit: I figured out what's going on.

    Groovy adds a method with a signature of identity(Closure c) to every object.

    This means that when you tried to dynamically invoke the identity element on the XML builder, while passing in a single closure argument, it was calling the identity() method, which is like calling delegate({...}) on the outer closure.

    Using the invokeMethod trick forces Groovy to bypass the Meta Object Protocol and treat the method as a dynamic method, even though the identity method already exists on the MetaObject.

    Knowing this, we can put together a better, more legible solution. All we have to do is change the signature of the method, like so:

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

    This is much more readable, it's clearer the intent, and the comment should (hopefully) prevent anyone from removing the "unnecessary" empty map.

    0 讨论(0)
提交回复
热议问题