5

我有这个代码:

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

它产生这个xml:

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

请注意,“身份”标签丢失了……我已经尝试了世界上所有的方法来让那个节点出现。我要把头发扯掉!

提前致谢。

4

1 回答 1

12

可能有更好的方法,但一个技巧是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();
}

这实际上是 Groovy 在幕后所做的。我无法上班delegate.identityowner.identity上班,这是惯用的伎俩。


编辑:我弄清楚发生了什么。

Groovy为每个对象添加了一个带有签名的方法。identity(Closure c)

这意味着当您尝试identity在 XML 构建器上动态调用元素时,在传入单个闭包参数时,它正在调用该identity()方法,这就像调用delegate({...})外部闭包一样。

使用这个invokeMethod技巧会迫使 Groovy 绕过 Meta Object Protocol 并将该方法视为动态方法,即使该identity方法已经存在于 MetaObject 上。

知道了这一点,我们就可以组合出一个更好、更清晰的解决方案。我们所要做的就是更改方法的签名,如下所示:

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

这更具可读性,意图更清晰,并且评论应该(希望)防止任何人删除“不必要的”空地图。

于 2012-07-09T04:20:14.110 回答