I'm trying to write a Velocity template that makes use of a DOM object. In particular my dependencies are Velocity 1.7 and Velocity-tools 2.0.
Following the documentation, this is how I pass the DOM to the template engine:
XmlTool tool = new XmlTool().parse(xmlString);
VelocityContext context = new VelocityContext();
context.put("xml", tool);
...
Let's say that this is my original XML document:
<root>
<foo>
<bar>
<baz>10</baz>
<qux>NO</qux>
</bar>
<bar>
<baz>20</baz>
<qux>YES</qux>
</bar>
<bar>
<baz>30</baz>
<qux>NO</qux>
</bar>
</foo>
</root>
And this my template file:
#if($xml.foo)
<document>
#foreach($bar in $xml.foo)
<repeatableElement>
<aaa>$bar.baz.text</aaa>
<bbb>$bar.qux.text</bbb>
</repeatableElement>
#end
</document>
Now my problem is, if I run this, the output looks like:
<document>
<repeatableElement>
<aaa>102030</aaa>
<bbb>NOYESNO</bbb>
</repeatableElement>
<repeatableElement>
<aaa>102030</aaa>
<bbb>NOYESNO</bbb>
</repeatableElement>
<repeatableElement>
<aaa>102030</aaa>
<bbb>NOYESNO</bbb>
</repeatableElement>
</document>
As you can see, the #foreach
loop correctly prints one repeatableElement
for each $bar
object. However the .text
method on $bar
children concatenates the text nodes of the siblings too!
What I want instead is to access each leaf text node alone:
<repeatableElement>
<aaa>10</aaa>
<bbb>NO</bbb>
</repeatableElement>
<repeatableElement>
<aaa>20</aaa>
<bbb>YES</bbb>
</repeatableElement>
...
Any tip is appreciated. Thanks!
This is a known problem of the Tools 2.0 XmlTool, whose getters call the JDOM method getPath()
instead of getUniquePath()
. So $bar.baz
returns all nodes.
To circumvent this problem, you can directly use the underlying JDOM API:
#if($xml.foo)
<document>
#foreach($bar in $xml.foo.children())
<repeatableElement>
<aaa>$bar.node().element('baz').text</aaa>
<bbb>$bar.node().element('qux').text</bbb>
</repeatableElement>
#end
</document>
#end
(Also note that you should loop on $xml.foo.children()
).