Golang use json in template directly

Darren Oakey picture Darren Oakey · Jul 18, 2016 · Viewed 14.9k times · Source

I'm looking for a way of binding json data directly into a template (without any struct representation in golang) - and I'm coming up short. Essentially, what I want is to have both template document and json to be just arbitrary data - and my handleFunc to essentially be:

func handler(writer http.ResponseWriter, request *http.Request) {
    t, _ := template.ParseFiles( "someTemplate.html" )
    rawJson, _ := ioutil.ReadFile( "someData.json" )

    // here's where I need help
    somethingTemplateUnderstands := ????( rawJson )

    t.Execute( writer, somethingTemplateUnderstands )
}

I've tried json.Unmarshal, but it seems to want a type. The main thing is, in the actual program both the json and the template come from the database and are completely changeable at runtime, (and there are lots of different ones) so I can't encode any structure in the go program itself. And obviously, I want to be able to make data like:

{ "something" : { "a" : "whatever" }}

and then a template like

<html><body>
    the value is {{ .something.a }}
</body></html>

Is this possible with the go http.template library, or do I need to go off to Node (or find another templating library?)

Answer

icza picture icza · Jul 18, 2016

You can use json.Unmarshal() to unmarshal a JSON text into a Go value.

And you can simply use a Go type interface{} to represent any arbitrary JSON value. Usually if it is a struct, map[string]interface{} is used and is more useful if you need to refer to values stored in it in Go code too (but this can't represent an array for example).

template.Execute() and template.ExecuteTemplate() take the data / parameters of the template as a value of type interface{} to which you can pass anything in Go. And the template engine uses reflection (reflect package) to "discover" its runtime type and navigate in it based on the selectors you provide in the template actions (which may designate fields of structs or keys in maps, or even method names).

Beyond that, everything works as expected. See this example:

func main() {
    t := template.Must(template.New("").Parse(templ))

    m := map[string]interface{}{}
    if err := json.Unmarshal([]byte(jsondata), &m); err != nil {
        panic(err)
    }

    if err := t.Execute(os.Stdout, m); err != nil {
        panic(err)
    }
}

const templ = `<html><body>
    Value of a: {{.something.a}}
    Something else: {{.somethingElse}}
</body></html>`

const jsondata = `{"something":{"a":"valueofa"}, "somethingElse": [1234, 5678]}`

Output (try it on the Go Playground):

<html><body>
    Value of a: valueofa
    Something else: [1234 5678]
</body></html>