Конфликт имени в Groovy MarkupBuilder

У меня есть этот код:

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>

Обратите внимание, что тег "identity" отсутствует... Я перепробовал все в мире, чтобы этот узел появился. Я рву свои волосы!

Заранее спасибо.

1 ответ

Решение

Там может быть лучший способ, но одна хитрость заключается в том, чтобы позвонить 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.identity или же owner.identity работать, которые являются обычными уловками.


Редактировать: я понял, что происходит.

Groovy добавляет метод с подписью identity(Closure c) к каждому объекту.

Это означает, что когда вы пытались динамически вызвать identity элемент XML Builder, передавая один аргумент замыкания, он вызывал identity() метод, который похож на вызов delegate({...}) на внешнем закрытии.

С использованием invokeMethod хитрость заставляет Groovy обходить протокол мета-объектов и рассматривать метод как динамический метод, даже если identity метод уже существует в Meta Object.

Зная это, мы можем составить лучшее, более четкое решение. Все, что нам нужно сделать, это изменить сигнатуру метода следующим образом:

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

Это намного более читабельно, это более ясно намерение, и комментарий должен (мы надеемся) препятствовать тому, чтобы кто-либо удалил "ненужную" пустую карту.

Другие вопросы по тегам