1.1 Top-level variables
Accessing top-level variables (those not defined in the template) is achieved in FreeMarker by just writing
the variable name between brackets and with a dollar sign at the start, for example : ${variable}. If the top-level variable is not defined,
FreeMarker will return an error and abort the template processing. Also, the name of a variable can only contain
letters, digits, underline (_), dollar sign ($), at sign (@) and hash (#) and must not start with a digit.
1.2 'Plain' variables
In FreeMarker it is possible to define new variables within the template. This is achieved with the assign directive :
Example
<#assign x = 1>
<#assign x = 3*x>
The output will be
1.3 Special variables
Special variables are variables defined by the FreeMarker engine itself and give general information about
the FreeMarker instance. A full list of the FreeMarker's special variables can be found at : http://docs.huihoo.com/freemarker/2.3.20/ref_specvar.html
String values can be defined between either quotation marks (") or apostrophe-quotes ('). There are some special characters that need
to be escaped in order to be part of the string. An exception to the list of escapable characters is the dollar sign ($), which requires
a special type of escaping obtained by prepending an r letter before the start of the string.
Example
${"Hello ${user}"}
${"I can escape with \\ ${user}"}
${r"Now I can read dollar signs $"}
The output will be
Hello deister
I can escape with \ deister
Now I can read dollar signs $
2.1 String operations
Strings can be concatenated with top-level variables or other strings just by connecting them with the
plus sign or just by simply writing the top-level variable inside curly brackets and with a dollar sign
at the start.
Accessing a single character of a string is achieved by simply writing the index of the character between brackets
as if the string was a sequence.
Example
${"Hello" + user}
${"Hello" + "World"}
${user[0]}
${user[0..4]}
This will output
Hellodeister
HelloWorld
deist
3.1 Numbers
Numerical values are defined just by typing in the number without quotation marks. Plus (+) and minus (-)
symbols are accepted at the beginning of the number, and the decimal separator is defined with a point (.)
Example
${-3} ${+4} ${-5.00} ${006} ${3.1415}
This will output
-3 4 -5 6 3.1415
3.2 Booleans and logical operations
Boolean types are defined by writing 'false' or 'true', without quotation marks
As for the logical operations, the equal operator is simply '=' (or '==' in java / C), while not equal is '!='. For numbers,
we also have '<=' and '=>' . Finally, the logical expressions 'and' and 'or' are simply '&&' and '||' respectively
Sequences are analogous to the classic concept of arrays, present in other programming languages.
This way, sequences contain a list of subvalues which can be any of the other in this section (string, number, sequence, hash...).
Its definition requires the subvariables to be put inside square brackets and this to be put inside a list
element as follows : <#list ["Joe", "Fred","Julia", "Kate"] ></#list>. Also, items inside
lists are technically expressions, so arithmetical or logic expressions will be evaluated if inserted in a sequence element.
Concatenation of sequences can be done with the same syntax that the concatenation of strings uses, that is, connect the sequence
elements to concatenate with a plus sign (+). To access a single element of the sequence we use the index of the element between brackets.
In case we want to access multiple elements of the sequence with a single statement, we can write the first and the last element
separated by two single points.
Finally to run over each element of a sequence we must define a loop variable at the end of the declaration of the list that will
store the current element of the sequence for the current iteration of the loop. For example :
Example
<#list ["Joe", "Fred"] + ["Julia", "Kate"] as user>
${user}
</#list>
The output will be
Julia
Hashes are defined as key/value pairs similar to those of a map.
Each key contains a single value, which can be any of the above (number, string, boolean...). The key must be
written between quotation marks and followed by a colon and its value. Each key/value pair will be separated
from the others by a comma. To access a hash value, we can either put a point followed by the key of the value
(hash.key) or enter the key between brackets and quotation marks (hash["key"]). In addition, concatenation of
hashes is structurally exact to sequence and string concatenation.
Example
<#assign ages = {"Joe":23, "Fred":25} + {"John":30, "Julia":18}>
Joe is ${ages.Joe}
Fred is ${ages.Fred}
Julia is ${ages.Julia}
John is ${ages["John"]}
The output will be
Joe is 23
Fred is 25
Julia is 18
John is 30
Built-ins are small functionalities that are usually applied to format variables. These are used after the variable to which they apply
and a question mark, for example: var?builtin. Although the complete list of built-ins can be found at : http://docs.huihoo.com/freemarker/2.3.20/ref_builtins.html we'll
review some of them in detail.
6.1 String Built-ins
cap_first: Returns the string with the first letter converted to upper case
lower_case / upper_case : Returns the string converted to upper/lower case
trim : Returns the string without any leading and trailing white-spaces.
Example
<#assign company=" deister ">
${company?cap_first}
${company?lower_case}
${company?upper_case}
${company?trim}
The output will be
Deister
deister
DEISTER
deister
<#assign friends=["Joe", "Fred","Julia", "Kate"]>
${friends?size}
${friends?seq_contains("Kate")?string("yes", "no")}
The output will be
Throughout axional Studio, data (resulting from a query for example) is usually presented as java RestultSets. ResultSets are json objects
that most importantly have a rows property containing all the data and a metadata property containing information of the type of the data.
It is convenient, then, to have some methods defined that allow us to process this type of information. The most important methods
available are:
getRows() : Returns the rows property of the resultSet.
getCols() : Returns the metadata (columns) property of the resultset.
getHeaders() : Returns the columns headers if any. (hasHeaders() returns true or false if they exist or not).
hasHeaders() : Returns whether the resultSet has headers or not(
getTitle() : Returns the title of the resultSet, if any.
col(resultset, columnIndex) : Returns the column object with matching with columnIndex.
isNullable() : Returns whether the column can be of value null
isRightAligned() : Returns whether the columns has its content right-aligned or not for display
getColumnName() : Returns the name of the column.
getColumnLabel() : Returns the label of the column.
getColumnType() : Returns the type of the column.
getColumnStyle() : Returns the style of the columns, including font color, background color...
getColumnsHref() : Returns the hyperlink, if any, related to the column.
Most of the time, we would only want to run over all the data (rows) of the resultset. This
can be achieved with the following example :
Example
Considering the minimal resultset :
"message": null,
"stackTrace": null,
"type": "SELECT",
"time": 3,
"errorCode": 0,
"SQLState": null,
"user": "deister",
"dbms": "demo_ftl_resultset",
"serial": 0,
"serial4": 0,
"serial8": 0,
"warnings": {
"message": null,
"stackTrace": ""
"sql": "select * from demo_ftl_resultset",
"stdOut": null,
"stdErr": null,
"count": 0,
"resultSet": {
"rowset": [
["IL", "Peoria", 8700.0],
["MA", "Boston", 6900.0],
["MD", "Rockville", 92970.0],
["NC", "Morrisville", 16500.0],
["NC", "Winston-Salem", 19200.0],
["WA", "Seattle", 26100.0]
"total": 6,
"maxrows": -1,
"metadata": [{
"columnName": "state",
"catalogName": " ",
"columnClassName": "java.lang.String",
"columnDisplaySize": 30,
"columnViewSize": 2,
"columnLabel": "state",
"columnType": 12,
"columnTypeName": "varchar",
"precision": 30,
"scale": 0,
"schemaName": " ",
"tableName": "demo_ftl_resultset",
"isNullable": 1,
"isRowid": false,
"isNumeric": false
"columnName": "city",
"catalogName": " ",
"columnClassName": "java.lang.String",
"columnDisplaySize": 30,
"columnViewSize": 18,
"columnLabel": "city",
"columnType": 12,
"columnTypeName": "varchar",
"precision": 30,
"scale": 0,
"schemaName": " ",
"tableName": "demo_ftl_resultset",
"isNullable": 1,
"isRowid": false,
"isNumeric": false
"columnName": "value",
"catalogName": " ",
"columnClassName": "java.math.BigDecimal",
"columnDisplaySize": 32,
"columnViewSize": 8,
"columnLabel": "value",
"columnType": 3,
"columnTypeName": "decimal",
"precision": 32,
"scale": 0,
"schemaName": " ",
"tableName": "demo_ftl_resultset",
"isNullable": 1,
"isRowid": false,
"isNumeric": true
"attributes": {}
Let's say we want to build a table with the data in it. We can do so with the following FTL :
<#list resultSet.getRows() as row>
<#list row.getCols() as col>
${row[col.getColumnName()]}
</#list>
</#list>
We would obtain
<td>IL</td><td>Peoria</td><td>8700.0</td>
<td>MA</td><td>Boston</td><td>6900.0</td>
<td>MD</td><td>Rockville</td><td>92970.0</td>
<td>NC</td><td>Morrisville</td><td>16500.0</td>
<td>NC</td><td>Winston-Salem</td><td>19200.0</td>
<td>WA</td><td>Seattle</td><td>26100.0</td>
An error will occur and abort the template processing if you try to access a missing variable.
However two special operators can suppress this error, and handle the problematic situation. The handled variable can be
top-level variable, hash subvariable, or sequence subvariable as well. Furthermore these operators handle the situation
when a method call doesn't return a value (from the viewpoint of Java programmers: it returns null or it's return type is void),
so it's more correct to say that these operators handle missing values in general, rather than just missing variables.
For those who know what's Java null, FreeMarker 2.3.x treats them as missing values. Simply, the template language doesn't
know the concept of null. For example, if you have a bean that has a maidenName property, and the value of that property is null,
then that's the same as if there were no such property at all, as far as the template is concerned (assuming you didn't configured
FreeMarker to use some extreme object wrapper, that is). The result of a method call that returns null is also treated as a missing
variable (again, assuming that you use some usual object wrapper).
8.1 Default value operator
Synopsis: unsafe_expr!default_expr
or unsafe_expr!
or (unsafe_expr)!default_expr
or (unsafe_expr)!
This operator allows you to specify a default value for the case when the value is missing.
The default value can be any kind of expression, so it doesn't have to be a string. For example you can write hits!0 or colors!["red", "green", "blue"].
There is no restriction regarding the complexity of the expression that specifies the default value,
for example you can write: cargo.weight!(item.weight * itemCount + 10).
Example
Assume no variable called mouse is present:
${mouse!"No mouse."}
<#assign mouse="Jerry">
${mouse!"No mouse."}
The output will be
No mouse.
Jerry