Categories
Programming

XML entity escaping in Freemarker and Looping issues

Thanks to my friend Chris Brelski for this trick. There are two ways to escape XML entities in Freemarker.

1)

<#assign xmlEscape="freemarker.template.utility.XmlEscape"?new()>
<#-- Use a custom directive: -->
<@xmlEscape> ${variable} 

2)

<#-- escape XML entities in output: -->
<#assign c="${category}">
<#escape c as c?xml>
        ${c}

In both these cases, there’s no need to modify Java files, and will convert & to

&

with proper encoding, etc.

Issues with Freemarker looping

I’ve been having lots of issues with looping in freemarker. Freemarker only has one basic loop, the collection loop which uses [#list] to iterate.
The List API reference only gives you two variables with this loop: list_index and list_has_next.
The problem is that if this were a true iterator, it should provide a next() method to provide the next iteration.
Say, you have some markup like this:

 
[#list data.outerInputContainer as inputContainers]
[#list inputContainers as innerInputContainer] [some freemarker macro that generates some markup here] [/#list]
[/#list]

So the backing Java object is a list of lists (the outer one called outerInputContainer and the inner one called innerInputContainer), and each inner list contains some input (radio input, select input, text input etc). In this situation, I want to bundle these inputs together into a group, so the List can be generic like

List

, where BaseInput is the superclass of radio, select, text input. So the list of lists look like this:

[[Radio1,Select1,Text1],[Radio2,Select2,Text2],[Radio3,Select3,Text3]]

We know, the order is always going to be Radio, Select, Text because the Java method always inserts the BaseInputs into the list in that order. However in Freemarker, since there’s no next() method, each iteration cannot be controlled. That means there’s no way to customize specific markup for radio, select or text. Instead, I have to put a generic container outside of those inputs. What I want to have is a way to customize:

 
[#list data.outerInputContainer as inputContainers] [#list inputContainers as innerInputContainer] [#if innerInputContainer.type = radio input] [some freemarker macro that generates some radio specific markup here] [/#if] [#if innerInputContainer.type = select input] [some freemarker macro that generates some select specific markup here] [/#if] [#if innerInputContainer.type = text input] [some freemarker macro that generates some text specific markup here] [/#if] [/#list] [/#list]

So I can either use conditionals here to get the type of input specific markup I want, if the iterator provides that. I don’t think Freemarker will like equals operator with custom objects, so I’ll probably have to create a method and delegate that to Java to return true or false. If Freemarker provided a next() method for list like iterator interfaces are supposed to do, then I can do all this logic in Freemarker, not Java. Or I have to put a generic container over all these inputs.

Anyways, there’s no way to modify the index like in a for loop with Freemarker, and so accessing a specific element of the list at List[i] is not really possible. I don’t like this inflexibility and I’m sure there’s a better way to do this. Feel free to comment if you’re a freemarker guru.

Edit:
You can access specific elements of a freemarker list using array index access instead of using an iterator:

innerInputContainer[0]
innerInputContainer[1]
innerInputContainer[2]