I have this code:
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();
}
It produces this xml:
<catalog xmlns='http://www.sybrium.com/XMLSchema/NodeCatalog'>
<groupId>sample.group</groupId>
<artifactId>sample-artifact</artifactId>
<version>1.0.0</version>
</catalog>
Notice that the "identity" tag is missing... I've tried everything in the world to get that node to appear. I'm ripping my hair out!
Thanks in advance.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With