does freemarker support show all variable in data-model? - freemarker

I want to see all variables in freemarker data-model, just like struts2 debug tag to show value stack.
Is there a way for freemarker to do this ?

There's no universal solution possible for that, but you can try
<#list .data_model?keys as key>
${key}
</#list>
This works if the data-model is just a usual Map or JavaBean, but for more sophisticated data-models it's up to the data-model implementation if it supports ?keys and if it indeed returns everything.
You also have the variables that you set in the templates, which can be listed like above, only instead of .data_model use .globals, .namespace (which means the current template namespace) and .locals.
You may also have Configuration-level shared variables, and there's no way to list those purely from FTL (you could write a custom TemplateMethodModel for it that reads Configuration.getSharedVariableNames() though, and call it from the template).
Of course, ideally, FreeMarker should have a <#show_variables> directive or something, that does a best effort to show all this... but sadly there is no such thing yet.

An even more detailed way would be this macro:
<#macro dump_object object debug=false>
<#compress>
<#if object??>
<#attempt>
<#if object?is_node>
<#if object?node_type == "text">${object?html}
<#else><${object?node_name}<#if object?node_type=="element" && object.##?has_content><#list object.## as attr>
${attr?node_name}="${attr?html}"</#list></#if>>
<#if object?children?has_content><#list object?children as item>
<#dump_object object=item/></#list><#else>${object}</#if> </${object?node_name}></#if>
<#elseif object?is_method>
#method
<#elseif object?is_sequence>
[<#list object as item><#dump_object object=item/><#if !item?is_last>, </#if></#list>]
<#elseif object?is_hash_ex>
{<#list object as key, item>${key?html}=<#dump_object object=item/><#if !item?is_last>, </#if></#list>}
<#else>
"${object?string?html}"
</#if>
<#recover>
<#if !debug><!-- </#if>LOG: Could not parse object <#if debug><pre>${.error}</pre><#else>--></#if>
</#attempt>
<#else>
null
</#if>
</#compress>
</#macro>
<#dump_object object=.data_model/>
This gives you a full dump of your data model.

Here is #lemhannes macro definition modified to emit JSON. Lightly tested on a fairly simple datamodel
<#macro dump_object object debug=false>
<#compress>
<#if object??>
<#attempt>
<#if object?is_node>
<#if object?node_type == "text">${object?json_string}
<#else>${object?node_name}<#if object?node_type=="element" && object.##?has_content><#list object.## as attr>
"${attr?node_name}":"${attr?json_string}"</#list></#if>
<#if object?children?has_content><#list object?children as item>
<#dump_object object=item/></#list><#else>${object}</#if>"${object?node_name}"</#if>
<#elseif object?is_method>
"#method"
<#elseif object?is_sequence>
[<#list object as item><#dump_object object=item/><#if !item?is_last>, </#if></#list>]
<#elseif object?is_hash_ex>
{<#list object as key, item>"${key?json_string}":<#dump_object object=item/><#if !item?is_last>, </#if></#list>}
<#else>
"${object?string?json_string}"
</#if>
<#recover>
<#if !debug>"<!-- </#if>LOG: Could not parse object <#if debug><pre>${.error}</pre><#else>-->"</#if>
</#attempt>
<#else>
null
</#if>
</#compress>
</#macro>

Related

Check if FreeMarker #nested directive is empty

I want to output tags around a <#nested> directive in a macro, but only if it would actually output something. The actual use case is more complicated, this is just the broken down version.
How do I check for existence of <#nested> content?
<#macro opt tagname>
<#if (#nested)??> <-- what do I need to put here
<${tagname}>
<#nested>
</${tagname}>
</#if>
</#macro>
Example 1
Template: <#opt hello />
Output: (empty)
Example 2
Template: <#opt hello>goodbye</#opt>
Output: <hello>goodbye</hello>
You have to capture the nested content, and then print it if necessary. Like this (this assumes auto-escaping on):
<#macro opt tagname>
<#local nestedContent><#nested></#local>
<#if nestedContent?has_content>
<${tagname}>${nestedContent}</${tagname}>
</#if>
</#macro>
Without auto-escaping the #if changes to just <#if nestedContent != ''>.

Freemarker - is nested if allowed inside else (not the common if elseif...elseif else)

I am not asking about the common nesting in freemarker (which I know for sure is supported) :
<#if cond1> do abc
<#elseif cond2> do xxx
<#elseif cond3> do yyy
<#else> do zzz
</#if>
Want to know if the below nesting is supported in freemarker:
<#if cond1> do abc
<#else>
<#if cond-X> do xxx </#if>
<#if cond-Y> do yyy </#if>
<#if cond-Z> do zzz </#if>
</#if>
Note that I have multiple if conditions inside the else.
My code throws error
Error detail: Syntax error in template "template" in line 1282, column 45: Unexpected directive, "</#if>". Check if you have a valid #if-#elseif-#else structure.
So I suspect the latter type of nesting provided by my architect.
It is supported, and what you show is parsed successfully for me. Maybe the error is elsewhere, or you aren't looking at the template that's actually processed?

Determine If a String Is Present in a List or Map?

How do I determine if a list or map contains a specific string? For example (pseudo code):
<#if listofItems.contains("random-string") >
the map contains a key called random-string
</#if>
Lists
If it's a list:
<#if listOfItems?seq_contains("random-string")>
...
</#if>
Maps
If it's a map:
<#if someMap["random-string"]??>
...
</#if>
If it's a map and the key contains no special characters:
<#if someMap.randomString??>
...
</#if>
If it's a map and you are looking for the value of a key-value pair:
<#if someMap?values?seq_contains("random-string")>
...
</#if>
I think something of this sort should work
<#if listofItems['random-string']?? >
you are inside if block
</#if>

How to DUMP object in freemarker ( .ftl )

is there a way how to dump whole object and write it somewhere?
Like:
var_dump() in php
console.log in JS
I found something like list, so I try something like this below:
<#list calculation as c>
${c}
</#list>
But template fall with error. I appriciate any advise!
It depends on the type of object you are iterating through. You can check the type of data your variable is and then output it appropriate (Reference: http://freemarker.incubator.apache.org/docs/ref_builtins_expert.html#ref_builtin_isType)
Here are some examples:
<#if calculation?is_sequence>
<#list calculation as c>
${c}
</#list>
<#elseif calculation?is_hash_ex>
<#list calculation?keys as key>
${key} - ${calculation[key]}
</#list>
<#elseif calculation?is_string>
${calculation}
</#if>
Take a look at https://github.com/ratherblue/freemarker-debugger/blob/master/debugger.ftl for more examples on dumping data

freemarker looping sequence error

I'm trying to retrieve data with this code
<tests>
<#if tests?exists>
<#list tests as object >
<test>
<#list object?keys as key >
<${key}>
<#if object[key]?exists>
<#if object[key]?is_hash> HASH
<#elseif object[key]?is_sequence>
<#list object[key] as hashKey>
</#list>
<#else> ${object[key]}</#if><#else>null
</#if>
</${key}>
</#list>
</test>
</#list>
</#if>
but getting an error ?size is unsupported for: freemarker.ext.beans.SimpleMethodModel
but <#elseif object[key]?is_sequence> sequence returns sequence. As I understand means that my object[key] is a sequence.
Any ideas?
That error occurs because as a result of some historical mishap, Java methods are sequences (with the built in ObjectWrapper-s, that is), but they don't implement ?size. (They are sequences so that foo.m[x] is equivalent with foo.m(x)). Add && !something?is_method to the condition avoid this.

Resources