How to get a map or list of template ‘actions’ from a parsed template?

Foreword: As Voker suggests, the Template.Tree field is “exported only for use by html/template and should be treated as unexported by all other clients.”

You should not rely on such thing to be able to provide input for a template execution. You must know the template you want to execute along with the data it expects. You shouldn’t “explore” it at runtime to provide arguments for it.


The value you get out of parsing a template is template.Template (either text/template or html/template, they have the same API). This template represents the templates as a tree of type parse.Tree. Everything that a text template contains is stored in this tree in nodes, including static texts, actions etc.

Having said that, you can walk this tree and look for nodes that identify such actions that access fields or call functions. The nodes are of type parse.Node which has a Node.Type() method returning its type. The possible types are defined as constants in the parse package, next to the parse.NodeType type, e.g.

const (
        NodeText    NodeType = iota // Plain text.
        NodeAction                  // A non-control action such as a field evaluation.
        NodeBool                    // A boolean constant.
        NodeChain                   // A sequence of field accesses.
        NodeCommand                 // An element of a pipeline.
        NodeDot                     // The cursor, dot.

        NodeField      // A field or method name.
        NodeIdentifier // An identifier; always a function name.
        NodeIf         // An if action.
        NodeList       // A list of Nodes.
        NodeNil        // An untyped nil constant.
        NodeNumber     // A numerical constant.
        NodePipe       // A pipeline of commands.
        NodeRange      // A range action.
        NodeString     // A string constant.
        NodeTemplate   // A template invocation action.
        NodeVariable   // A $ variable.
        NodeWith       // A with action.
)

So here is an example program that recursively walks a template tree, and looks for nodes with NodeAction type, which is “A non-control action such as a field evaluation.”

This solution is just a demonstration, a proof of concept, it does not handle all cases.

func ListTemplFields(t *template.Template) []string {
    return listNodeFields(t.Tree.Root, nil)
}

func listNodeFields(node parse.Node, res []string) []string {
    if node.Type() == parse.NodeAction {
        res = append(res, node.String())
    }

    if ln, ok := node.(*parse.ListNode); ok {
        for _, n := range ln.Nodes {
            res = listNodeFields(n, res)
        }
    }
    return res
}

Example using it:

t := template.Must(template.New("cooltemplate").
    Parse(`<h1>{{ .name }} {{ .age }}</h1>`))
fmt.Println(ListTemplFields(t))

Output (try it on the Go Playground):

[{{.name}} {{.age}}]

Leave a Comment