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

匆匆过客 提交于 2019-12-01 07:26:53

问题


So I would like to somehow get all of my {{ .blahblah }} actions defined in a template as slice of strings.

For example if I have this template:

<h1>{{ .name }} {{ .age }}</h1>

I would like to be able to get []string{"name", "age"}. Pretend that a template has the method func (t *Template) Fields() []string:

t := template.New("cooltemplate").Parse(`<h1>{{ .name }} {{ .age }}</h1>`)
if t.Fields() == []string{"name", "age"} {
    fmt.Println("Yay, now I know what fields I can pass in!")
    // Now lets pass in the name field that we just discovered.
    _ = t.Execute(os.Stdout, map[string]string{"name": "Jack", "age":"120"})
}

Is there a way to inspect a parsed template like this?
Thanks!


回答1:


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}}]


来源:https://stackoverflow.com/questions/40584612/how-to-get-a-map-or-list-of-template-actions-from-a-parsed-template

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!