根据Go标准库中的documentation包的text/template
,(据我所知,html/template
在这里将是相同的)只需使用管道运算符将吐出“默认文本表示”,无论是什么:
{{管道}}
The default textual representation of the value of the pipeline is copied to the output.
在地图的情况下,你会得到一个很好的打印格式,其中包含键名和所有内容......顺便说一句,这是有效的JavaScript,因此如果你愿意,它可以很容易地将整个结构传递给你的JS代码。
我的问题是,这个文本表示是如何确定的,更具体地说,我可以加入它吗?我想也许它会检查管道是否是fmt.Stringer
,我可以给我的地图子类型一个String() string
方法,但似乎并非如此。我正在寻找text/template
代码,但我似乎不知道它是如何做到的。
text/template
如何确定“默认文本表示”?
默认文本表示确定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
的指针,它将无法工作。并且因为那时只有*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)
获取转义:
<html>
这是在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
另请注意,如果值实现error
,则将调用Error()
方法,它优先于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上尝试一下。