Fork me on GitHub


  • Simple & Friendly
  • High-Performance
  • High-Quality


HTTL This document details the core functionality, if you intend to use HTTL development, please read.

test sample template syntax: src/test/resources/comment/templates

See test output results same file: src/test/resources/comment/results

basic syntax

and Velocity similar and different points see:Velocity comparison

		#if (books)
			#for (Book book: books)
$ {book.title}

comment syntax

instruction on both sides can put HTML comments, to avoid interference with native HTML page.

HTTL at parse time, it will automatically remove the comment symbol Directive edge of HTML. (The default is on filter)

$ {book.title}
Using HTML

default comment character: (default, no configuration)


If you use HTTL generate Java code, you can also read:


attribute syntax

based Html tag attributes: (instruction and expressions with the same comment syntax)

$ {book.title}

need to configure:

template.filters + = httl.spi.filters.AttributeSyntaxFilter

attribute syntax need to use jericho packet parsing HTML tags:



conflict if the property and other frameworks, you can add the namespace:

attribute.namespace = httl

namespace wording such as:

place without the label, you can use the above comment syntax.

Compatible Syntax

HTTL offer compatible Velocity syntax support, just configuration: (from version 1.0.8 supports)

template.filters + = httl.spi.filters.VelocitySyntaxFilter

in compatibility mode, you can still use the standard syntax HTTL facilitate the gradual migration.


HTTL only #set, #if, #else, #for, #break, #macro six directives, as well as output placeholders and notes escape, keeping the minimum instruction set, and they will not increase the instruction.

does not recognize the command name will be text output, such as HTML color: # FF00EE.

If the command name and text

phase, such as: #elseTEXT, available without parameters separated by parentheses, eg: #if (x) AAA #else() BBB #end() CCC

output instructions

$ {} filter output

output expression evaluates, and filtered, such as: filter variable HTML tags.

$ {Expression}

$ {}

Note: HTTL default opened EscapeXmlFilter, to prevent HTML injection attacks, see:Security Samples. If you need stronger filtering, please self-fulfilling Filter, and configured to value.filters. Here the runtime hotspot note performance.

If the output variable of type Template, then the default is not filtered, for example: $ {include ("foo.httl")}

$! {} does not filter output

expression evaluates as output, without any filtering, usually used to output HTML fragment.

$! {Expression}

$! {Body}

Note: Do not filter output, make sure that the content is the developer of secure content determined, not by the user-submitted content, in order to prevent HTML injection attacks, see:Security Samples.

variable command

#set variable type

declare a variable of type, other types of variables within the template based on this type of derivation.

#set (type name, type name)

#set (User user, Listbooks)

Note: only supports List and Map of a generic, multi-level generic able to resolve, but not derived.

If there is a global variable can be used to configure the global import variable type declaration, there is no need for each template declaration:

import.variables + = User loginUser

default has been imported: (can be used directly in the template)

import.variables = Context parent, Template super, Template this, Engine engine

If you are using the built-in MVC integration, integration has been in default import some common variables associated, as describedConfiguration - Import variable type declaration< / a>a.

If undeclared variable, default Object: (If it is a direct output variables, may declare type)

default.variable.type = java.lang.Object
$ {obj} // direct output, do not take the property, do not do arithmetic, no declared type

If your system, most of the String type operation, you can also change the default type String:

default.variable.type = java.lang.String

repeatedly to set the variable's type, if the type of parent-child relationship to subtype Priority:

#set (ParentClass var1)
#set (ChildClass var1)

regardless of the order, all with subtype Priority:

#set (ChildClass var1)
#set (ParentClass var1)

if it is two completely different types, error:

#set (OriginClass var1)
#set (DiffrentClass var1)

#set variable assignment

the expression evaluates into variable.

#set (name = expression)
#set (type name = expression)

#set (name: = expression)
#set (type name: = expression)

#set (price = book.price *
#set (int price = book.price *

Note that in order to simplify the writing template, #set template variables are all valid, no restrictions within the instruction in the block:

#if (xxx)
#set (var="value") // Variable full template effectively, rather than if the block is valid
$ {Var} // can be accessed within the block if the value of var

not like Java:

#set (var = null)
#if (xxx)
#set (var="value")
$ {Var}

type declarations, and can be used as casts, for example:

#set (Book book = bookentry.value)

If bookentry.value type of loss, the above wording can recover book type.

a set command may also have more than one type declarations or assignments, separated by commas, but the type declaration and assignment to write separately, such as:

 #set (User user, List  books)
#set (price = book.price, discount =

assignment will generate a local variable, it writes the current Context in If there include() sub-template, the template can also be in the child read the variable.

If you want to get in the parent template, template variables include the child, or you want to get after rendering the template variables Due to template rendering finished, it Context has pop(), so the need to write variables in the template will be higher Conetxt to read on the outside. If you need to use the Context assigned to the higher, you can use":="assignment, it will be variable write Context.getParent() in For example:

#set (price: = book.price *

you can render the template, through Context.getContext(). get ("price"); got the above variables.


// you can put into the reference set unmodifiable Map, will not affect the operation.
Mapparameters = Collections.unmodifiableMap (parameters);

// Incoming parameters in the rendering process will not always be modified,
// Make sure the rendering process without side effects, as well as multiple rendering idempotent.
template.render (parameters, writer);

// Template #set (price = x) variables are put into the Context of the temporary collection.
Context.getContext(). Put ("price", x);

// First #set temporary collection lookup, then the native incoming parameters, locate and then to search on a Context.
Context.getContext(). Get ("price");

conditional instructions

#if condition

If the conditional expression evaluates to true or a non-empty, then the output contained in the instruction blocks.

Note: For non-Boolean value: non-zero number, non-empty string, non-empty, non-null object, is true.

#if (expression)

#if (user.role =="admin")

#else condition otherwise

If the preceding #if condition is not true, then the output #else directive contains blocks.

Note: #else directives can be directly without conditions, to reduce the #if condition combinations.

#else (expression)

#if (user.role =="admin")
#else (user.role =="member")

loop instructions

#for loop

resulting collection iteration expression, for each value in the set, repeatedly outputs the block contained in the instruction.

#for (name: expression)
#for (type name: expression)

#for (book: books)
	$ {For.index}
	$ {For.size}
	$ {For.first}
	$ {For.last}

type declarations, and can be used as casts, for example:

#for (Book book: booklist)
	$ {Book.title}

If booklist generic lost, the above wording can recover book type.

in the iteration before, you can do the collection operations, such as:

# # Perform nine times
#for (9)

# # Literal sequence set, a digital output 1-9
#for (i: 1 .. 9)

# # Literal discrete set, 10,20,30 three digital outputs
#for (i: [10, 20, 30])

# # Take a non-empty iteration, if books1 is not empty, then iterative books1, otherwise iteration books2
#for (book: books1 | | books2)

# #set summed, then iterative
#for (book: books1 + books2)

# #set sort, then iterative
#for (book: books.sort)

# # Recursive iterations, such as Menu has a getChildren() method returns the child list:
#for (Menu menu: menus.recursive ("getChildren"))

#break cycle interrupts

When the conditional expression is true or non-empty interrupt the current iteration.

Note: #break conditional parameters directly: #break (i ​​== j), need not be written: #if (i == j) #break #end

#break (expression)

#for (book: books)
	#break (for.index == 10)

#else loop or

If the previous #for collection is empty, then the output #else directive contains blocks.

Note: #for #else directives can be directly used in combination, can reduce the #if not empty determine the conditions of writing.

#else (expression)

#for (book: books)

template directives

#macro template fragment

the instruction block encapsulated into reusable template fragments, which can be passed as a variable, you can repeat the output can be inherited overwritten.

#macro (name)
#macro (name (name, name))
#macro (name (type name, type name))

#macro (xxx)

$ {Xxx} to execute the macro variables
$ {Xxx (arg1, arg2)} method to execute the macro

template inheritance macro coverage, see:inheritance example

while the macro definition, you can define the position while the output macro or the macro assigned to the variable, and define parameters for the macro to simplify the writing.

#macro ($ name)
#macro ($ name (name, name))
#macro ($ name (type name, type name))

#macro (var = name)
#macro (var = name (name, name))
#macro (var = name (type name, type name))

#macro ($ name =>cache)
#macro ($ name (name, name) =>cache)
#macro ($ name (type name, type name) =>cache)

#macro (var = name =>cache)
#macro (var = name (name, name) =>cache)
#macro (var = name (type name, type name) =>cache)

#macro ($ xxx)

#macro (xxx = xxx)

portfolio syntax equivalence relation:

#macro ($ xxx)

Is equivalent to: (a position in the macro definition output simultaneously)

#macro (xxx)
$ {Xxx}
#macro (aaa = xxx)

Is equivalent to: (performed while the macro definition, and assigns the result to the specified variable)

#macro (xxx)
#set (aaa = xxx)
#macro ($ xxx =>cache)

Is equivalent to: (a position in the macro definition output simultaneously)

#macro (xxx)
$ {Cache (xxx)}
#macro (aaa = xxx =>cache)

Is equivalent to: (performed while the macro definition, and assigns the result to the specified variable)

#macro (xxx)
#set (aaa = cache (xxx))

#break interrupt template

When the conditional expression is true or empty, interrupting the current template or macro execution.

Note: Do not #for Directives #break, indicates that an interrupt template or macro execution.

#break (expression)

#break (debug)

comment directive

# # line comment

hidden line comment content, with a newline character, for the annotation process, or shielded instruction content.

# # Line comment

# # This is line comment

# ** # block comment

hide content block comment can contain line breaks, for the annotation process, or shielded instruction content.

# * Block comment * #

# *
	 This is block comment
* #

escaped directive

# [] # do not parse block

template content is output to output plain text content, block or bulk escape special characters.

# [No parse block] #

# [This is no parse block: #if $ {name}] #

\ # \ $ special character escape

instruction is output as special characters, used to output plain text content.

\ #, \ $, \ \

\ # Xxx
\ $ {Xxx}
\ \ $ {Xxx}


based on Java expressions and extension methods.

supports Java all expressions, the following list only different points in Java:

  • operations are all null values ​​returns null, for example: $ {}, if foo is null, all subsequent operations ultimately is null, and not a null pointer.
  • double equal sign"=="will be parsed equals() methods, rather than than the memory address.
  • single and double quotation marks will produce the string: 'a' or"a" is of type String, if you want to declare literal char, please use backticks: `a` as char type.
  • plus sign"+" digital first, $ {1 +"2"} output 3, instead of 12, string concatenation try to use $ {s1} $ {s2}, rather than $ {s1 + s2}
  • Bean property getter method will resolve to call, $ {} is equivalent to $ {user.getName()}
  • all implement Comparable objects support comparison operators, such as: #if (date1
  • logic and supported by all objects or, respectively, returns null or non-null value, for example: $ {list1 | | list2}, if list1 is not empty then return list1, otherwise return list2.
  • List and Map can be bracketed values, such as: list [0] is equivalent to list.get (0), map ["abc"] is equivalent to map.get ("abc")
  • uppercase 3L will generate java.lang.Long value, lowercase 3l will generate long values.
  • support is operator with instanceof same source C #.

attribute lookup order to $ {} as an example: (compile-time decisions, does not affect performance)

  • first checks for any type of imported obj foo() static method
  • then find obj.getFoo() function
  • then find obj.isFoo() function
  • then find function
  • then find properties

operator expression

set operator

$ {list [0]} is equivalent to: $ {list.get (0)}

$ {} is equivalent to: $ {map.get ("abc")}

$ {Map ["a.b.c"]} is equivalent to: $ {map.get ("a.b.c")}

Sequence generation: an .. 3
For example:
#for (i: 1 .. 10)
$ {I}

List generated: [123,"abc", var]
For example:
#for (color: ["red","yellow","blue"])
$ {Color}

Map generator: ["xxx": 123,"yyy":"abc","zzz": var]
For example: (this Map maintaining declaration order)
#for (entry: ["red":"# FF0000","yellow":"# 00FF00"])
$ {Entry.key} = $ {entry.value}

Collection added: list1 + list2
For example:
#for (item: list1 + list2)
$ {Item}

logical operators

#if (object)
Is equivalent to:
#if (object! = Null)

#if (string)
Is equivalent to:
#if (string! = null && string.length()>0)

#if (list)
Is equivalent to:
#if (list! = Null && list1.size()>0)

#for (item: list1 | | list2)
Is equivalent to:
#for (item: list1! = Null && list1.size()>0? List1: list2)

Date Operator

date1>= date2

function expression

transformation function ("")
str.toDate ("yyyy-MM-dd HH: mm: ss")

aggregate function

Array and List size method can be used to obtain the same size

#for (item: list.sort)

#set (colors = ["red","blue","green"]. ToCycle)
#for (item: list)
$ {}

file functions

inherited template to the current template macro, replace the parent of the same name in the template macro, execute the parent template, the output to the current location.
$ {Extends ("/ layout.httl")}
$ {Extends ("/ layout.httl","UTF-8")}
$ {Extends (".. / Layout.httl")}
$ {Extends (".. / Layout.httl","UTF-8")}

Contains a template, perform the target template, the output to the current location.
$ {Include ("/ template.httl")}
$ {Include ("/ template.httl","UTF-8")}
$ {Include ("/ template.httl", ["arg":"value"])}
$ {Include (".. / Template.httl")}
$ {Include (".. / Template.httl","UTF-8")}

Template contains macros, perform target macro output to the current position.
$ {Include ("/ template.httl #macro")}
$ {Include ("/ template.httl #macro","UTF-8")}
$ {Include ("/ template.httl #macro")}
$ {Include ("/ template.httl #macro", ["arg":"value"])}

Read the contents of the target file, output to the current position.
$ {Read ("/ text.txt")}
$ {Read ("/ text.txt","UTF-8")}

internationalization function

$ {"key". message} or $ {message ("key")}
In localized = true when looking for the following sequence of key configuration files:

$ {Include ("template.httl")}
In localized = true, the order find the following files are present:
template.httl related configuration:

# international prefix configuration file information, from the Loader looks / WEB-INF/
message.basename = / WEB-INF/messages

# Internationalized message formats, support for message and string
# Correspond MessageFormat.format() and String.format()
message.format = message

# Users can save files in UTF-8 international information, without the need ascii2native
message.encoding = UTF-8

# Open international search
localized = true

# Default Regional Information
locale = zh_CN

formatting function

num.format ("# # #, # # 0")
num.format ("# # #, # # 0. # #")
date.format ("yyyy-MM-dd")
date.format ("yyyy-MM-dd HH: mm: ss")

escape function


JSON function

# the object into a JSON string

# Parse the JSON string into a Map object

# Parse the JSON string into an object
str.decodeJson (""). to ("")

default transcoding using the built-in parser JSON string.

To use fastjson

transcoding, configure:

json.codec = httl.spi.codecs.FastjsonCodec



XML function

# the object into XML string

# Parse the XML string into an object ("")

used by default java.beans.XMLEncoder transcoding.

To use xstream

transcoding, configure:

xml.codec = httl.spi.codecs.XstreamCodec




digest function

# generate MD5 code

# Generate SHA code

# Generate a summary of the specified type code
str.digest ("MD5")
str.digest ("SHA")

naming conversion function

# Convert underscores to separate naming

# Turn into a hump-separated name (first letter lowercase)

# To uppercase separated naming (first letter capitalized)

system function

# current time
$ {Now()}

# Random number
$ {Random()}

# Unique code

Velocity comparison

If you used a Velocity template, you can view the following comparison, a better understanding of:

syntax contrast

  1. HTTL command variable without $ symbol, which only supports #if (x == y), does not support the #if ($ x == $ y), because the command string is not quoted variables, and conventional language syntax, like add $ bit rubbish, but also easy to forget to write.

  2. HTTL placeholder necessary to increase the brackets, only support $ {aaa}, does not support the $ aaa, because $ is legal in JavaScript variable name symbols, and $ {} is not to reduce the confusion, but also to prevent more human development, it was to increase the brackets, some without, simply did not have a choice, are added, consistent.

  3. HTTL placeholder when the variable is null output blank string, unlike Velocity command is output as the original, ie $ {aaa}, is equivalent to the Velocity $ {aaa}, so as not to forget to write Developers exclamation, expression leak source, for as output, use the escape \ $ {aaa}, In HTTL in, $ {aaa} represents not content filtering for output as HTML fragments.

  4. HTTL support all local variables used for expression evaluation, that is, you do not like the Velocity as the first #set ($ j = $ i + 1) to a temporary variable, and then output the temporary variable $ {j}, HTTL can be directly output $ {i + 1}, other commands, too, such as: #if (i + 1 == n).

  5. HTTL method using extended Class native ways, such as: $ {"a". toChar}, rather than the Velocity Tool utility methods like: $ (StringTool.toChar ("a")), such call method is more intuitive, more in line with the code writing habits.

Directive contrast

Velocity HTTL similarities function change
$ {xxx.yyy}
$ xxx.yyy
$ {xxx.yyy} same output placeholder HTTL braces required
$! {xxx.yyy}
$! xxx.yyy
$! {xxx.yyy} different null value does not show source VM null value does not show source
HTTL to not filter output
# # ...
# * ... * #
# # ...
# * ... * #
same comment block is not displayed
# [[...]] # # [...] # similar not parse text block HTTL little pair of brackets
\ # \ $ \ \ \ # \ $ \ \ same special characters escaped
#set ($ xxx = $ yyy) #set (xxx = yyy)
#set (Type xxx = yyy)
#set (Type xxx)
same assign variables HTTL available with type declarations
#if ($ xxx == $ yyy) #if (xxx == yyy) same conditional
#elseif ($ xxx == $ yyy) #else (xxx == yyy) similar or conditional HTTL multiplexing #else directive
#else #else same otherwise determine
#end #end
#end (if)
same end instruction HTTL available with matching command names
#foreach ($ item in $ list) #for (item: list)
#for (Type item: list)
similar List cycle HTTL to Java format
#break #break
#break (xxx == yyy)
same interrupt cycle HTTL directly with conditions
# stop #break
#break (xxx == yyy)
similar stop template parsing HTTL multiplexing #break directive
#macro ($ xxx) #macro (xxx) different reusable template fragments macro VM as the macro instruction execution
HTTL performed as a function
# define ($ xxx) #macro (xxx = xxxmacro) similar catch block output to a variable HTTL multiplexing #macro directive
# include ("xxx.txt") $ {read ("xxx.txt")} similar read text file content HTTL to function extensions
# parse ("xxx.vm") $ {include ("xxx.httl")} similar contains another template output HTTL to function extensions
# evaluate ("$ {1 + 2}") $ {render ("$ {1 + 2}")} similar template evaluation HTTL to function extensions