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

missingkey=default (the default) / missingkey=invalid

Go’s <no value>

missingkey=zero

an empty string (what Helm expects)

missingkey=error

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

TemplateParseException

the template text is malformed (parse)

TemplateNotFoundException

no template with the given name exists (execute / render)

TemplateExecutionException

execution fails (bad pipeline, a function error, …)

FunctionExecutionException

a function signals failure; surfaced wrapped in a TemplateExecutionException

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.