Reputation: 2200
I have a recursive data structure, basically a tree, where a node may have child nodes and so on. I'm trying to generate a JSON-like file of that structure. For that thought of using the #parse
directive. In the context i store the root node and in templateName the template's name.
{
"name" = "$node.name",
"value" = "$node.value"
#if ($node.childrens.size() > 0)
,
"childrens" = {
#foreach ($child in $node.childrens)
## The next statement does not work
#parse ($child.type + ".vm", $child)
#end
}
#end
}
The apache velocity documentation states that the #parse
directive only takes one argument.
In examples I have seen the use of the #set
directive before calling another template, but if the depth of the tree is higher than 2 this does not work because the variable used in the #set
directive is stored in the same context so when going from depth 1 to 2 the variable would be overwritten.
The reason to use #parse
instead of a macro as suggested by @Sergiu Dumitriu is because each node may be rendered in a different way depending on it's property $node.type
. I would like to have a template for each type so one person adding a template for a particular type does not have to mess with any other template, I mean, maybe this could be achieved having a switch on the type property; but that implies that all ways of rendering will be defined in the same file.
Is there any way of using Velocity to apply templates to recursive data structures?
Solution Based on both answers from Sergiu Dumitriu the final template looks like:
#macro ( displayNode $node)
{
#set ( $parseNode = $node )
#set ( $parseTemplate = $parseNode.type + ".vm" )
#parse ( $parseTemplate )
#set ( $parseNode = $node )
#set ( $parseTemplate = "Common.vm" )
#parse ( $parseTemplate )
}
#end
The Common.vm
has the structure shown in the question.
Upvotes: 3
Views: 9918
Reputation: 11601
To overcome the fact that by default in Velocity variables are global, you could use local variables in the current scope. Velocity has several options for enabling different local scopes, including a template scope created whenever rendering a template, template.provide.scope.control
. The problem is that this is disabled by default, so you have to configure Velocity to have it activated. After you activate this, you'll automatically have a $template
variable which you can use to store local variables.
## The first thing to do is to copy the $node value into a local scope
## that won't be overwritten by nested calls
#set ($template.node = $node)
{
"name" = "$template.node.name",
"value" = "$template.node.value"##
#if ($template.node.childrens.size() > 0),
"childrens" = {
#foreach ($child in $template.node.children)
## $template.node doesn't change, so now $node can be freely reassigned
#set ($node = $child)
#parse("${child.type}.vm")
#end
}
#end
}
Upvotes: 3
Reputation: 11601
You should not use #parse
, since it's a somewhat costly operation. Define a macro and use it recursively instead.
#macro(displayNode $node)
{
"name" = "$node.name",
"value" = "$node.value"##
#if ($node.childrens.size() > 0),
"childrens" = {
#foreach ($child in $node.children)
#displayNode($child)
#end
}
#end
}
#end
If the template name is also variable, you can use #evaluate
to construct the template name dynamically:
#set ($d = '$')
#foreach ($child in $node.children)
#evaluate("#display${child.type}Node(${d}child)")
#end
Upvotes: 7