Template guide
bafi templates are Go text/template templates: plain text with
actions in {{ }}. Everything outside the braces is copied to the output as it is. The examples below
use this input:
{
"company": "Example Ltd",
"users": [
{"name": "Alice", "age": 34, "roles": ["admin", "dev"], "my-id": "A1"},
{"name": "Bob", "age": 19, "roles": ["dev"], "my-id": "B2"}
]
}
In VS Code the gotemplate-syntax extension adds highlighting.
Accessing data
The dot . is the current data; at the start it is the whole input. Follow keys with more dots:
bafi -i users.json -t '?{{.company}} has {{len .users}} users'
Example Ltd has 2 users
{{index .users 0}}- an item of a list (counting from 0).{{index . "my-id"}}- keys containing-, spaces or other special characters needindex.{{toJSON .users}}- print any part of the data, which is the quickest way to find a path.$is always the root, even insiderangeorwith:{{$.company}}.
XML specifics
XML becomes data like this:
- attributes are keys with a
-prefix:<order id="7">→{{index .order "-id"}}; - the text of an element that also has attributes is under
#text:{{index .total "#text"}}; - namespace prefixes are dropped:
<soap:Body>→.Envelope.Body; - an element that occurs once is a single value, several occurrences are a list. Use
mustArraybeforerangewhen it can be either:{{range mustArray .orders.order}}; - all values are text; convert with
toInt,toFloat64or the...fmath functions.
See XML to JSON for a complete example.
Loops and conditions
{{range .users -}}
{{.name}} ({{index . "my-id"}}): {{if ge .age 21.0}}adult{{else}}junior{{end}}, roles: {{range $i, $r := .roles}}{{if $i}}, {{end}}{{$r}}{{end}}
{{end -}}
bafi -i users.json -t list.tmpl
Alice (A1): adult, roles: admin, dev
Bob (B2): junior, roles: dev
{{range .list}}...{{end}}repeats for every item; inside,.is the item.{{range $i, $item := .list}}also gives the position.{{if condition}}...{{else if other}}...{{else}}...{{end}}.{{with .value}}...{{end}}runs only when the value exists and sets.to it.- Comparisons:
eq,ne,lt,le,gt,ge; combine withand,or,not. Compare values of the same kind: JSON numbers arefloat64, so compare with21.0or convert ({{if ge (toInt .age) 21}}).
Whitespace control
A dash inside the braces removes whitespace and line breaks on that side: {{- trims before, -}}
trims after. In the loop above, {{range .users -}} drops the line break after the range line and
{{end -}} the one after end, so each user produces exactly one line.
{{- .name -}}
Variables
{{$total := 0.0}}
{{range .items}}{{$total = addf $total .price}}{{end}}
Total: {{$total}}
:= creates a variable, = changes it. Variables live until the {{end}} of the block they were
created in.
Functions
Functions are written before their arguments ("prefix" or Polish notation):

| Math | Template |
|---|---|
| 1 + 2 | {{add 1 2}} |
| (1 + 2) * 3 | {{mul (add 1 2) 3}} |
| 6 / 2 * (1 + 2) | {{div 6 (mul 2 (add 1 2))}} → 1, {{mul (div 6 2) (add 1 2)}} → 9 |
The order of operations is always explicit, inside out, so there is no doubt about precedence. Values are converted automatically where possible:
{"items": ["item-0", "item-1", "item-2"]}
bafi -i items.json -t '?{{range .items}}{{print "item-" (add1 (trimPrefix . "item-"))}} {{end}}'
item-1 item-2 item-3
The pipe | passes a value as the last argument of the next function: {{.name | upper}} equals
{{upper .name}}.
All functions with examples: Function reference. Your own functions: Lua.
Comments
{{/* a comment */}}
{{- /* a comment that also trims the whitespace around it */ -}}
Reusing parts of a template
{{define "user"}}{{.name}} <{{.email}}>{{end}}
{{range .users}}{{template "user" .}}
{{end}}
Escaping for the output format
bafi writes values exactly as they are; it does not know whether the output is HTML, XML, CSV, SQL or a shell script. Escape where the format needs it:
| Output | Use |
|---|---|
| JSON | toJSON for text values, or toJSON on the whole structure |
| XML, HTML | html on every value, or toXML |
| CSV | quotes around values, replaceAll "\"" "\"\"" inside, csvSafe against formulas |
| SQL | replaceAll "'" "''" inside single-quoted values |
See Security notes.