html / template:如何在没有<script>标记的情况下转义JavaScript(JSON)?

The following program writes

<html><body>Hello <script>[{"A":"foo","B":"bar"},{"A":"bar","B":"baz"}]</script></body></html>

because of the <script>-Tag (which does some JavaScript JSON to string encoding). How can I get the same without the <script>-Tag?. That is: I'd like to write

t, err := template.New("foo").Parse("<html><body>Hello <pre>{{.}}</pre></body></html>
")

and get

<html><body>Hello <pre>[{"A":"foo","B":"bar"},{"A":"bar","B":"baz"}]</pre></body></html>

back? I have seen the | ... syntax for contexts in the templates package, but which context should I use?

package main

import (
    "html/template"
    "log"
    "os"
)

func main() {

    type keyvalue struct {
        A, B string
    }

    a := []keyvalue{{"foo", "bar"}, {"bar", "baz"}}

    t, err := template.New("foo").Parse("<html><body>Hello <script>{{.}}</script></body></html>
")
    if err != nil {
        log.Fatal(err)
    }
    err = t.ExecuteTemplate(os.Stdout, "foo", a)
    if err != nil {
        log.Fatal(err)
    }
}

Background: I need to generate an HTML attribute for the X-Editable JavaScript library, which looks like this: source="[{value: 1, text: 'text1'}, {value: 2, text: 'text2'}, ...]"

If you have "special needs" regarding the generated HTML you might use text/template.

But maybe in your case it would be better to just convert a to a string representation first and stuff this string into the rendering. I think this makes it much clearer what you are trying to do.

Or: Write your own filter function (see http://golang.org/pkg/html/template/#Template.Funcs) which does this string processing an call your function in the template.

I'm really not sure if this is what you want, but anyway:

package main

import (
        "html/template"
        "log"
        "os"
)

func main() {

        type keyvalue struct {
                A, B string
        }

        a := []keyvalue{{"foo", "bar"}, {"bar", "baz"}}

        t, err := template.New("foo").Parse("<html><body>Hello <pre>{{. | printf `%q` }}</pre></body></html>
")
        if err != nil {
                log.Fatal(err)
        }
        err = t.ExecuteTemplate(os.Stdout, "foo", a)
        if err != nil {
                log.Fatal(err)
        }
}

Playground


Output:

<html><body>Hello <pre>[{&#34;foo&#34; &#34;bar&#34;} {&#34;bar&#34; &#34;baz&#34;}]</pre></body></html>

And the <pre> fragment renders as

[{"foo" "bar"} {"bar" "baz"}]

in a browser.

You can do two things:

  • Within the template use the js function (easy)
  • Pass the parameter as JSON string and prevent escaping by telling go that it's HTML:

Consider this: http://play.golang.org/p/ETvuxYXR1M

package main

import (
    "encoding/json"
    "html/template"
    "log"
    "os"
)

func main() {

    type keyvalue struct {
        A, B string
    }

    a := []keyvalue{{"foo", "bar"}, {"bar", "baz"}}

    tjs, err := template.New("tjs").Parse("<html><body>Hello <pre>{{js .}}</pre></body></html>
")
    if err != nil {
        log.Fatal(err)
    }
    tr, err := template.New("tr").Parse("<html><body>Hello <pre>{{.}}</pre></body></html>
")
    if err != nil {
        log.Fatal(err)
    }
    json, _ := json.MarshalIndent(a, "", " ")
    err = tr.ExecuteTemplate(os.Stdout, "tr", template.HTML(json))
    err = tjs.ExecuteTemplate(os.Stdout, "tjs", a)
    if err != nil {
        log.Fatal(err)
    }
}

Output:

<html><body>Hello <pre>[
 {
  "A": "foo",
  "B": "bar"
 },
 {
  "A": "bar",
  "B": "baz"
 }
]</pre></body></html>
<html><body>Hello <pre>[{foo bar} {bar baz}]</pre></body></html>