text/template issue Parse() vs. ParseFiles()

Evan Plumlee picture Evan Plumlee · Aug 4, 2012 · Viewed 15.2k times · Source

I'm trying to do some simple work with the text/template package. The sample given at the top of template is what I'm working with.

How do I write the 'parsed' file so template.ParseFiles() properly reads and executes it?

package main

import (
    "text/template"
    "os"
)

type Inventory struct {
    Material string
    Count    uint
}

func main() {
    sweaters := Inventory{"wool", 17}
    tmpl, err := template.New("test").Parse("{{.Count}} items are made of {{.Material}}")
    // tmpl, err := template.New("test").ParseFiles("file.txt")

    if err != nil { panic(err) }
    err = tmpl.Execute(os.Stdout, sweaters)
    if err != nil { panic(err) }
}

/*
Contents of file.txt:
{{.Count}} items are made of {{.Material}}

Error thrown:
panic: template: test:1: "test" is an incomplete or empty template

goroutine 1 [running]:
main.main()
    /tmp/templates/t.go:19 +0x21a

goroutine 2 [syscall]:
created by runtime.main
    /var/tmp/portage/dev-lang/go-1.0.1/work/go/src/pkg/runtime/proc.c:221
*/

I have a copy of this code posted at the golang playground here

Edit #1: I've been doing some research on this issue... since it's the Execute() method that actually throws the exception, and not the ParseFiles() part, I checked the method definition:

// Execute applies a parsed template to the specified data object,
// and writes the output to wr.
func (t *Template) Execute(wr io.Writer, data interface{}) (err error) {
    defer errRecover(&err)
    value := reflect.ValueOf(data)
    state := &state{
        tmpl: t,
        wr:   wr,
        line: 1,
        vars: []variable{{"$", value}},
    }
    if t.Tree == nil || t.Root == nil {
        state.errorf("%q is an incomplete or empty template", t.name)
    }
    state.walk(value, t.Root)
    return
}

So, on a hunch, I dumped the value of t.Tree for the inline 'non-file' style, tmpl is: &parse.Tree{Name:"test", Root:(*parse.ListNode)(0xf840030700), funcs:[]map[string]interface {}(nil), lex:(*parse.lexer)(nil), token:[2]parse.item{parse.item{typ:6, val:""}, parse.item{typ:9, val:"{{"}}, peekCount:1, vars:[]string(nil)} and when ran with ParseFiles(), tmpl is: (*parse.Tree)(nil). I find it odd that one is a dereference, and one value is a pointer. This may help solve the riddle

Answer

Eli Revah picture Eli Revah · Aug 4, 2012
sweaters := Inventory{"wool", 17}
tmpl, err := template.ParseFiles("file.txt")
if err != nil {
    panic(err)
}
err = tmpl.ExecuteTemplate(os.Stdout, "file.txt", sweaters)
if err != nil {
    panic(err)
}

If you have many files, you can use ParseGlob:

tmpl, err := template.ParseGlob("*.txt")
if err != nil {
    panic(err)
}
err = tmpl.ExecuteTemplate(os.Stdout, "file.txt", sweaters)
if err != nil {
    panic(err)
}
err = tmpl.ExecuteTemplate(os.Stdout, "file2.txt", sweaters)
if err != nil {
    panic(err)
}