The default textual representation is determined by how the fmt
package prints the value. So you were barking up the right tree.
See this example:
t := template.Must(template.New("").Parse("{{.}}"))
m := map[string]interface{}{"a": "abc", "b": 2}
t.Execute(os.Stdout, m)
It outputs:
map[a:abc b:2]
Now if we use a custom map type with a String()
method:
type MyMap map[string]interface{}
func (m MyMap) String() string { return "custom" }
mm := MyMap{"a": "abc", "b": 2}
t.Execute(os.Stdout, mm)
Output is:
custom
Try these (and below examples) on the Go Playground.
What to look out for?
Note that MyMap.String()
has a value receiver (not a pointer). And I pass a value of MyMap
, so it works. If you change the receiver type to pointer to MyMap
, it won't work. And it is because then only a value of type *MyMap
will have a String()
method, but not a value of MyMap
.
If the String()
method has a pointer receiver, you have to pass &mm
(a value of type *MyMap
) if you want your custom representation to work.
Also note that in case of html/template
, the template engine does contextual escaping, so the result of the fmt
package may further be escaped.
For example if your custom String()
method would return something "unsafe":
func (m MyMap2) String() string { return "<html>" }
Trying to insert it:
mm2 := MyMap2{"a": "abc", "b": 2}
t.Execute(os.Stdout, mm2)
Gets escaped:
<html>
Implementation
This is where it is implemented in the text/template
package: text/template/exec.go
, unexported function state.PrintValue()
, currently line #848:
_, err := fmt.Fprint(s.wr, iface)
If you're using the html/template
package, it's implemented in html/template/content.go
, unexported function stringify()
, currently line #135:
return fmt.Sprint(args...), contentTypePlain
Further options
Also note that if the value implements error
, the Error()
method will be called and it takes precedence over 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)
Will output:
custom-error
Instead of custom
. Try it on the Go Playground.