Getting started
1. Add the dependency
For the engine plus the Sprig functions:
org.alexmond
gotmpl4j-sprig
1.3.0
The Sprig module depends on gotmpl4j-core transitively and registers its functions
automatically through ServiceLoader. If you want the engine with Go built-ins only,
depend on gotmpl4j-core instead.
2. Render a template
import org.alexmond.gotmpl4j.GoTemplate;
import java.io.StringWriter;
import java.util.Map;
var tpl = new GoTemplate(); // auto-loads Sprig from the classpath
tpl.parse("greet", "Hello {{ .name | upper }}!");
var out = new StringWriter();
tpl.execute("greet", Map.of("name", "world"), out);
// -> "Hello WORLD!"
See the GoTemplate API
for every method, and MissingKeyMode
for the missingkey option below.
render is a convenience that returns the string directly:
String s = new GoTemplate()
.parse("greet", "Hello {{ .name | title }}!")
.render("greet", Map.of("name", "world")); // "Hello World!"
3. Data binding
The "dot" (.) is the current data value. Fields resolve against Map keys, JavaBean
getters, and public fields:
record User(String name, int age) {}
new GoTemplate()
.parse("u", "{{ .name }} is {{ .age }}")
.render("u", new User("Ada", 36)); // "Ada is 36"
4. Controlling function loading
By default new GoTemplate() discovers every FunctionProvider on the classpath. Use the
builder for finer control:
GoTemplate tpl = GoTemplate.builder()
.noAutoDiscovery() // skip ServiceLoader discovery
.htmlEscaping() // enable contextual HTML auto-escaping
.withFunctions(Map.of("greet", args -> "hi " + args[0]))
.build();
5. Missing and nil values
How a nil or absent value renders in a bare action is controlled by the missingkey
option, mirroring Go’s Template.Option:
new GoTemplate().parse("t", "x={{ .missing }}").render("t", Map.of());
// default -> "x=<no value>" (Go's text/template behavior)
new GoTemplate().option("missingkey=zero") // empty string instead of <no value>
.parse("t", "x={{ .missing }}").render("t", Map.of());
// -> "x=" (Helm semantics)
| Option | A nil/absent action renders as |
|---|---|
|
Go’s |
|
an empty string (what Helm expects) |
|
execution fails |
This only affects bare actions like {{ .x }}; the print/printf functions keep Go’s
fmt semantics (a nil argument prints <nil>).
6. Reusing a parsed template
Parsing is the expensive step; execution is cheap. Parse once and execute many times — a
parsed GoTemplate (without HTML escaping) is safe to execute concurrently. For a
pre-resolved handle to one named template, use compiled:
var compiled = tpl.parse("row", "{{ .id }},{{ .name }}\n").compiled("row");
for (var record : records) {
compiled.execute(record, writer);
}
7. Error handling
Every failure the engine raises is an unchecked
GoTemplateException,
so one catch covers everything; catch a subtype when you need to tell failures apart:
| Exception | Raised when |
|---|---|
|
the template text is malformed ( |
|
no template with the given name exists ( |
|
execution fails (bad pipeline, a function error, …) |
|
a function signals failure; surfaced wrapped in a |
try {
return new GoTemplate().parse(name, text).render(data);
}
catch (GoTemplateException ex) {
// one root for parse + execution + function failures
log.warn("template {} failed: {}", name, ex.getMessage());
throw ex;
}
Authors of custom functions signal failure by throwing
FunctionExecutionException
(unchecked); the engine wraps it as a TemplateExecutionException when it surfaces from
execute/render.