How to use golang template missingkey option?

huangapple go评论92阅读模式
英文:

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)
	}
}

https://play.golang.org/p/0_ZZ2Pwa1uZ

答案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

huangapple
  • 本文由 发表于 2016年2月25日 04:30:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/35612456.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定