1

根据Go 标准库中包的文档,(据我所知,这里是相同的)简单地使用管道运算符将吐出任何内容的“默认文本表示”:text/templatehtml/template

{{管道}}

The default textual representation of the value of the pipeline is copied to the output.

在地图的情况下,你会得到一个很好的打印格式,其中包含键名和所有内容......顺便说一下,这是有效的 JavaScript,因此如果你愿意,它可以很容易地将整个结构传递到你的 JS 代码中。

我的问题是,这个文本表示是如何确定的,更具体地说,我可以加入它吗?我想也许它会检查管道是否是 afmt.Stringer并且我可以给我的地图子类型一个String() string方法,但似乎并非如此。我正在寻找text/template代码,但我似乎错过了它是如何做到这一点的。

如何text/template确定“默认文本表示”?

4

1 回答 1

3

默认文本表示由fmt包打印值的方式决定。所以你在正确的树上吠叫。

看这个例子:

t := template.Must(template.New("").Parse("{{.}}"))
m := map[string]interface{}{"a": "abc", "b": 2}
t.Execute(os.Stdout, m)

它输出:

map[a:abc b:2]

现在,如果我们使用带有String()方法的自定义地图类型:

type MyMap map[string]interface{}

func (m MyMap) String() string { return "custom" }

mm := MyMap{"a": "abc", "b": 2}
t.Execute(os.Stdout, mm)

输出是:

custom

在Go Playground上尝试这些(以及以下示例)。

需要注意什么?

请注意,它MyMap.String()有一个值接收器(不是指针)。我传递了一个值MyMap,所以它可以工作。如果将接收器类型更改为指向 的指针MyMap,它将不起作用。这是因为只有 type 的值*MyMap才有String()方法,而不是MyMap.

如果该方法有一个指针接收器,如果您希望您的自定义表示工作String(),您必须传递&mm(类型的值)。*MyMap

另请注意,在 的情况下html/template,模板引擎会进行上下文转义,因此fmt包的结果可能会进一步转义。

例如,如果您的自定义String()方法将返回“不安全”的内容:

func (m MyMap2) String() string { return "<html>" }

试图插入它:

mm2 := MyMap2{"a": "abc", "b": 2}
t.Execute(os.Stdout, mm2)

逃脱:

&lt;html&gt;

执行

这是它在text/template包中实现的地方:text/template/exec.go,未导出的函数state.PrintValue(),当前第 848 行:

_, err := fmt.Fprint(s.wr, iface)

如果您正在使用该html/template包,它是在html/template/content.go未导出的函数中实现的stringify(),当前第 135 行:

return fmt.Sprint(args...), contentTypePlain

更多选项

另请注意,如果该值实现errorError()则将调用该方法并且它优先于String()

type MyMap map[string]interface{}

func (m MyMap) Error() string { return "custom-error" }

func (m MyMap) String() string { return "custom" }

t := template.Must(template.New("").Parse("{{.}}"))
mm := MyMap{"a": "abc", "b": 2}
t.Execute(os.Stdout, mm)

将输出:

custom-error

而不是custom. 在Go Playground上尝试一下。

于 2016-07-22T06:57:29.720 回答