英文:
How to use golang template missingkey option?
问题
我会为你翻译以下内容:
我期望以下代码返回一个错误,如果映射变量'd'中没有键'/foo/bar',但实际上一切都正常。我是否漏掉了什么?
这是Go Playground上的代码示例:
http://play.golang.org/p/Oqg1Dy4h1k
英文:
I would expect
t, err := template.New("t1").Option("missingkey=error").Parse("{{index . \"/foo/bar\"}}")
err = t.Execute(os.Stdout, d)
to return an error if the map 'd' doesn't have a key '/foo/bar', but everything is just fine. Am I missing something?
Here's the go playground:
http://play.golang.org/p/Oqg1Dy4h1k
答案1
得分: 2
missingkey
选项在使用index
时不起作用。只有当你使用.<field-name>
访问映射时,才能得到期望的结果:
t, err := template.New("t1").Option("missingkey=error").Parse(`{{.foo}}`)
err = t.Execute(os.Stdout, d)
你可以通过定义自己的索引函数来解决这个问题,当键缺失时返回一个错误:
package main
import (
"errors"
"fmt"
"os"
"text/template"
)
func lookup(m map[string]interface{}, key string) (interface{}, error) {
val, ok := m[key]
if !ok {
return nil, errors.New("missing key " + key)
}
return val, nil
}
func main() {
d := map[string]interface{}{
"/foo/bar": 34,
}
fns := template.FuncMap{
"lookup": lookup,
}
t, err := template.New("t1").Funcs(fns).Parse(`{{ lookup . "/foo/baz" }}`)
if err != nil {
fmt.Printf("ERROR 1: %q\n", err)
}
err = t.Execute(os.Stdout, d)
if err != nil {
fmt.Printf("ERROR 2: %q\n", err)
}
}
链接:https://play.golang.org/p/0_ZZ2Pwa1uZ
英文:
The missingkey
option does not work with index
. You will only get the desired result when you access the map using .<field-name>
:
t, err := template.New("t1").Option("missingkey=error").Parse(`{{.foo}}`)
err = t.Execute(os.Stdout, d)
You can get around this by defining your own index function that returns an error when a key is missing:
package main
import (
"errors"
"fmt"
"os"
"text/template"
)
func lookup(m map[string]interface{}, key string) (interface{}, error) {
val, ok := m[key]
if !ok {
return nil, errors.New("missing key " + key)
}
return val, nil
}
func main() {
d := map[string]interface{}{
"/foo/bar": 34,
}
fns := template.FuncMap{
"lookup": lookup,
}
t, err := template.New("t1").Funcs(fns).Parse(`{{ lookup . "/foo/baz" }}`)
if err != nil {
fmt.Printf("ERROR 1: %q\n", err)
}
err = t.Execute(os.Stdout, d)
if err != nil {
fmt.Printf("ERROR 2: %q\n", err)
}
}
答案2
得分: 1
你好 @user5976738 和大家。
我有一个不需要模板函数的不同解决方案,它完全使用了 text/template/parse
包。
这个解决方法对你们中的一些人可能非常有用。使用示例:
if missingKeys, ok := IsMissingKeys(err); ok{
for _, key := range missingKeys{ println(key) }
}
源代码:
package main
import (
"fmt"
"os"
"strings"
"strconv"
"text/template"
templateparse "text/template/parse"
)
type errMissingKeys struct {
Keys []string
TemplateName string
}
func (err errMissingKeys) Error() string {
return fmt.Sprintf("template: %s: map has no entry for keys: %s", err.TemplateName, strings.Join(err.Keys, ", "))
}
// IsMissingKeys reports whether an "err" is caused by template missing keys.
//
// Usage:
// if missingKeys, ok := IsMissingKeys(err); ok{
// for _, key := range missingKeys{ println(key) }
// }
func IsMissingKeys(err error) ([]string, bool) {
if err != nil {
if v, ok := err.(errMissingKeys); ok {
return v.Keys, true
}
}
return nil, false
}
func main() {
data := map[string]interface{}{
"bar": 34,
}
tmpl, err := template.New("myTemplate").Option("missingkey=error").Parse(`{{.baz}} {{.other}}`)
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
err = tmpl.Execute(os.Stdout, data)
if err != nil {
if strings.Contains(err.Error(), "map has no entry for key ") {
// Check if a field is not a "data" match,
// which will lead on execute error on first undefined (missingkey=error).
// So collect all these keys so we can have a typed error with all unknown keys listed.
var missingKeys []string
for _, n := range tmpl.Root.Nodes {
if n.Type() == templateparse.NodeAction {
key := strings.TrimFunc(n.String(), func(r rune) bool {
return r == '{' || r == '}' || r == ' ' || r == '.'
})
if key == "" {
continue
}
if _, ok := data[key]; !ok {
missingKeys = append(missingKeys, strconv.Quote(key))
}
}
}
// if just one then keep the original error which contains the full context,
// else set to custom error type which will keep the missing keys for caller's further use.
if len(missingKeys) > 1 {
err = errMissingKeys{TemplateName: tmpl.Name(), Keys: missingKeys}
}
}
fmt.Println(err.Error())
os.Exit(1)
}
}
Playground 链接: https://play.golang.org/p/JMwBpU2KyP2
最好的祝福,Gerasimos Maropoulos - https://iris-go.com 的创作者
英文:
Hello @user5976738 and all.
I have a different solution for that problem which does not require a template function at all and it uses the text/template/parse
package.
This workaround may be very useful for some of you. Example of usage:
if missingKeys, ok := IsMissingKeys(err); ok{
for _, key := range missingKeys{ println(key) }
}
Source Code:
package main
import (
"fmt"
"os"
"strings"
"strconv"
"text/template"
templateparse "text/template/parse"
)
type errMissingKeys struct {
Keys []string
TemplateName string
}
func (err errMissingKeys) Error() string {
return fmt.Sprintf("template: %s: map has no entry for keys: %s", err.TemplateName, strings.Join(err.Keys, ", "))
}
// IsMissingKeys reports whether an "err" is caused by template missing keys.
//
// Usage:
// if missingKeys, ok := IsMissingKeys(err); ok{
// for _, key := range missingKeys{ println(key) }
// }
func IsMissingKeys(err error) ([]string, bool) {
if err != nil {
if v, ok := err.(errMissingKeys); ok {
return v.Keys, true
}
}
return nil, false
}
func main() {
data := map[string]interface{}{
"bar": 34,
}
tmpl, err := template.New("myTemplate").Option("missingkey=error").Parse(`{{.baz}} {{.other}}`)
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
err = tmpl.Execute(os.Stdout, data)
if err != nil {
if strings.Contains(err.Error(), "map has no entry for key ") {
// Check if a field is not a "data" match,
// which will lead on execute error on first undefined (missingkey=error).
// So collect all these keys so we can have a typed error with all unknown keys listed.
var missingKeys []string
for _, n := range tmpl.Root.Nodes {
if n.Type() == templateparse.NodeAction {
key := strings.TrimFunc(n.String(), func(r rune) bool {
return r == '{' || r == '}' || r == ' ' || r == '.'
})
if key == "" {
continue
}
if _, ok := data[key]; !ok {
missingKeys = append(missingKeys, strconv.Quote(key))
}
}
}
// if just one then keep the original error which contains the full context,
// else set to custom error type which will keep the missing keys for caller's further use.
if len(missingKeys) > 1 {
err = errMissingKeys{TemplateName: tmpl.Name(), Keys: missingKeys}
}
}
fmt.Println(err.Error())
os.Exit(1)
}
}
Playground link: https://play.golang.org/p/JMwBpU2KyP2
Best Regards, Gerasimos Maropoulos - Creator of https://iris-go.com
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论