试图将一个Unmarshal配置文件Unmarshal到一个结构,使用viper返回这个错误:1 error(s) decoding:\n\n* 'NATS' expected a map, got 'slice'。少了什么?
守则:
func lab() {
var c conf
// config file
viper.SetConfigName("draft")
viper.AddConfigPath(".")
viper.SetConfigType("hcl")
if err := viper.ReadInConfig(); err != nil {
log.Error(err)
return
}
log.Info(viper.Get("NATS")) // gives [map[port:10041 username:cl1 password:__Psw__4433__ http_port:10044]]
if err := viper.Unmarshal(&c); err != nil {
log.Error(err)
return
}
log.Infow("got conf", "conf", c)
}
type conf struct {
NATS struct {
HTTPPort int
Port int
Username string
Password string
}
}以及配置文件(当前目录中的draft.hcl):
NATS {
HTTPPort = 10044
Port = 10041
Username = "cl1"
Password = "__Psw__4433__"
}编辑
使用hcl包检查了此结构,并正确地封送/解除封送。而且,这对yaml和viper也是正确的。
这两种名称为log.Info(viper.Get("NATS"))的地方有区别。当hcl版本返回一个映射片段时,yaml版本返回一个映射:map[password:__psw__4433__ httpport:10044 port:10041 username:cl1]。
发布于 2018-01-13 15:05:31
你的结构与HCL不匹配。当转换为json时,HCL如下所示
{
"NATS": [
{
"HTTPPort": 10044,
"Password": "__Psw__4433__",
"Port": 10041,
"Username": "cl1"
}
]
}所以Conf结构应该如下所示
type Conf struct {
NATS []struct{
HTTPPort int
Port int
Username string
Password string
}
}修改码
package main
import (
"log"
"github.com/spf13/viper"
"fmt"
)
type Conf struct {
NATS []struct{
HTTPPort int
Port int
Username string
Password string
}
}
func main() {
var c Conf
// config file
viper.SetConfigName("draft")
viper.AddConfigPath(".")
viper.SetConfigType("hcl")
if err := viper.ReadInConfig(); err != nil {
log.Fatal(err)
}
fmt.Println(viper.Get("NATS")) // gives [map[port:10041 username:cl1 password:__Psw__4433__ http_port:10044]]
if err := viper.Unmarshal(&c); err != nil {
log.Fatal(err)
}
fmt.Println(c.NATS[0].Username)
}发布于 2020-06-26 21:29:41
我知道这个问题已经两年多了,但最近我遇到了同样的问题。
我使用viper将不同的配置文件加载到Go结构中,允许在JSON、YAML、TOML、HCL中进行配置,只需选择您最喜欢的:)
HCL文件格式确实将映射包装到片中,因为它允许重新定义如下所示的节:
section = {
key1 = "value"
}
section = {
key2 = "value"
}这是其他格式不支持的东西。
我就是这样修好的:
// sliceOfMapsToMapHookFunc merges a slice of maps to a map
func sliceOfMapsToMapHookFunc() mapstructure.DecodeHookFunc {
return func(from reflect.Type, to reflect.Type, data interface{}) (interface{}, error) {
if from.Kind() == reflect.Slice && from.Elem().Kind() == reflect.Map && (to.Kind() == reflect.Struct || to.Kind() == reflect.Map) {
source, ok := data.([]map[string]interface{})
if !ok {
return data, nil
}
if len(source) == 0 {
return data, nil
}
if len(source) == 1 {
return source[0], nil
}
// flatten the slice into one map
convert := make(map[string]interface{})
for _, mapItem := range source {
for key, value := range mapItem {
convert[key] = value
}
}
return convert, nil
}
return data, nil
}
}configOption := viper.DecodeHook(mapstructure.ComposeDecodeHookFunc(
sliceOfMapsToMapHookFunc(),
mapstructure.StringToTimeDurationHookFunc(),
mapstructure.StringToSliceHookFunc(","),
))另外两个钩子是默认的,所以您可能希望保留它们。
然后,将该选项传递给解组方法。
viper.Unmarshal(&c, configOption)使用这种方法,您不需要在您的结构或映射周围切片。这也使它与其他配置文件格式兼容。
https://stackoverflow.com/questions/48240461
复制相似问题