Конфликт имени в 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();
}
Это намного более читабельно, это более ясно намерение, и комментарий должен (мы надеемся) препятствовать тому, чтобы кто-либо удалил "ненужную" пустую карту.