koanf是一个库,用于在GO应用程序中以不同格式的不同来源读取配置。它是SPF13/Viper的更清洁,更轻松的替代品,具有更好的抽象和可扩展性,依赖性较少。
koanf V2具有用于从各种来源读取配置的模块(提供商),例如文件,命令线标志,环境变量,库库和S3,以及用于解析(解析器)格式,例如JSON,YAML,TOML,TOML,Hashicorp HCl。很容易插入自定义解析器和提供商。
提供者和解析器中的所有外部依赖项均与核心分离,并且可以根据需要单独安装。
安装
koanf/v2
# Install the necessary Provider(s).
# Available: file, env/v2, posflag, basicflag, confmap, rawbytes,
# structs, fs, s3, appconfig/v2, consul/v2, etcd/v2, vault/v2, parameterstore/v2
# eg: go get -u github.com/knadh/ koanf /providers/s3
# eg: go get -u github.com/knadh/ koanf /providers/consul/v2
go get -u github.com/knadh/ koanf /providers/file
# Install the necessary Parser(s).
# Available: toml, toml/v2, json, yaml, dotenv, hcl, hjson, nestedtext
# go get -u github.com/knadh/ koanf /parsers/$parser
go get -u github.com/knadh/ koanf /parsers/toml\”>
# Install the core. go get -u github.com/knadh/ koanf /v2 # Install the necessary Provider(s). # Available: file, env/v2, posflag, basicflag, confmap, rawbytes, # structs, fs, s3, appconfig/v2, consul/v2, etcd/v2, vault/v2, parameterstore/v2 # eg: go get -u github.com/knadh/ koanf /providers/s3 # eg: go get -u github.com/knadh/ koanf /providers/consul/v2 go get -u github.com/knadh/ koanf /providers/file # Install the necessary Parser(s). # Available: toml, toml/v2, json, yaml, dotenv, hcl, hjson, nestedtext # go get -u github.com/knadh/ koanf /parsers/$parser go get -u github.com/knadh/ koanf /parsers/toml
请参阅所有捆绑的提供商和解析器的列表。
内容
- 概念
- 从文件读取配置
- 观看文件以进行更改
- 从命令行读取
- 阅读环境变量
- 读取原始字节
- 从地图和结构阅读
- 解除和编组
- 合并和关键案例灵敏度的顺序
- 定制提供商和解析器
- 定制合并策略
- 可安装的提供商和解析器列表
概念
-
koanf .Provider是一个通用接口,可提供配置,例如,来自文件,环境变量,HTTP源或任何地方。配置可以是解析器可以解析的原始字节,也可以是可以直接加载的嵌套map[string]interface{}。 -
koanf .Parser是一个通用接口,可以采用原始字节,解析并返回一个嵌套的map[string]interface{}。例如,JSON和YAML解析器。 - 一旦加载到koanf中,配置就会由界定的关键路径语法查询。例如:
app.server.port。可以选择任何定界符。 - 可以将来自多个源的配置加载并合并到koanf实例中,例如,首先从文件加载,并用命令行的标志覆盖某些值。
通过这两个接口实现, koanf可以从任何源以任何格式获得配置,并将其解析,并使应用程序可用于应用程序。
从文件读取配置
koanf/v2\”
\”github.com/knadh/ koanf /parsers/json\”
\”github.com/knadh/ koanf /parsers/yaml\”
\”github.com/knadh/ koanf /providers/file\”
)
// Global koanf instance. Use \”.\” as the key path delimiter. This can be \”/\” or any character.
var k = koanf .New(\”.\”)
func main() {
// Load JSON config.
if err := k.Load(file.Provider(\”mock/mock.json\”), json.Parser()); err != nil {
log.Fatalf(\”error loading config: %v\”, err)
}
// Load YAML config and merge into the previously loaded config (because we can).
k.Load(file.Provider(\”mock/mock.yml\”), yaml.Parser())
fmt.Println(\”parent\’s name is = \”, k.String(\”parent1.name\”))
fmt.Println(\”parent\’s ID is = \”, k.Int(\”parent1.id\”))
}
\”>
package main import ( \"fmt\" \"log\" \"github.com/knadh/ koanf /v2\" \"github.com/knadh/ koanf /parsers/json\" \"github.com/knadh/ koanf /parsers/yaml\" \"github.com/knadh/ koanf /providers/file\" ) // Global koanf instance. Use \".\" as the key path delimiter. This can be \"/\" or any character. var k = koanf . New ( \".\" ) func main () { // Load JSON config. if err := k . Load ( file . Provider ( \"mock/mock.json\" ), json . Parser ()); err != nil { log . Fatalf ( \"error loading config: %v\" , err ) } // Load YAML config and merge into the previously loaded config (because we can). k . Load ( file . Provider ( \"mock/mock.yml\" ), yaml . Parser ()) fmt . Println ( \"parent\'s name is = \" , k . String ( \"parent1.name\" )) fmt . Println ( \"parent\'s ID is = \" , k . Int ( \"parent1.id\" )) }
观看文件以进行更改
一些提供商揭示了一种Watch()方法,该方法使提供商手表更改配置并触发回调以重新加载配置。如果在koanf对象上执行Load()时发生同时发生的*Get()调用,这不是goroutine安全的。这种情况将需要静音锁定。
file, appconfig, vault, consul提供商具有Watch()方法。
koanf/v2\”
\”github.com/knadh/ koanf /parsers/json\”
\”github.com/knadh/ koanf /parsers/yaml\”
\”github.com/knadh/ koanf /providers/file\”
)
// Global koanf instance. Use \”.\” as the key path delimiter. This can be \”/\” or any character.
var k = koanf .New(\”.\”)
func main() {
// Load JSON config.
f := file.Provider(\”mock/mock.json\”)
if err := k.Load(f, json.Parser()); err != nil {
log.Fatalf(\”error loading config: %v\”, err)
}
// Load YAML config and merge into the previously loaded config (because we can).
k.Load(file.Provider(\”mock/mock.yml\”), yaml.Parser())
fmt.Println(\”parent\’s name is = \”, k.String(\”parent1.name\”))
fmt.Println(\”parent\’s ID is = \”, k.Int(\”parent1.id\”))
// Watch the file and get a callback on change. The callback can do whatever,
// like re-load the configuration.
// File provider always returns a nil `event`.
f.Watch(func(event interface{}, err error) {
if err != nil {
log.Printf(\”watch error: %v\”, err)
return
}
// Throw away the old config and load a fresh copy.
log.Println(\”config changed. Reloading …\”)
k = koanf .New(\”.\”)
k.Load(f, json.Parser())
k.Print()
})
// To stop a file watcher, call:
// f.Unwatch()
// Block forever (and manually make a change to mock/mock.json) to
// reload the config.
log.Println(\”waiting forever. Try making a change to mock/mock.json to live reload\”)
<-make(chan bool)
}\”>
package main import ( \"fmt\" \"log\" \"github.com/knadh/ koanf /v2\" \"github.com/knadh/ koanf /parsers/json\" \"github.com/knadh/ koanf /parsers/yaml\" \"github.com/knadh/ koanf /providers/file\" ) // Global koanf instance. Use \".\" as the key path delimiter. This can be \"/\" or any character. var k = koanf . New ( \".\" ) func main () { // Load JSON config. f := file . Provider ( \"mock/mock.json\" ) if err := k . Load ( f , json . Parser ()); err != nil { log . Fatalf ( \"error loading config: %v\" , err ) } // Load YAML config and merge into the previously loaded config (because we can). k . Load ( file . Provider ( \"mock/mock.yml\" ), yaml . Parser ()) fmt . Println ( \"parent\'s name is = \" , k . String ( \"parent1.name\" )) fmt . Println ( \"parent\'s ID is = \" , k . Int ( \"parent1.id\" )) // Watch the file and get a callback on change. The callback can do whatever, // like re-load the configuration. // File provider always returns a nil `event`. f . Watch ( func ( event interface {}, err error ) { if err != nil { log . Printf ( \"watch error: %v\" , err ) return } // Throw away the old config and load a fresh copy. log . Println ( \"config changed. Reloading ...\" ) k = koanf . New ( \".\" ) k . Load ( f , json . Parser ()) k . Print () }) // To stop a file watcher, call: // f.Unwatch() // Block forever (and manually make a change to mock/mock.json) to // reload the config. log . Println ( \"waiting forever. Try making a change to mock/mock.json to live reload\" ) <- make ( chan bool ) }
从命令行读取
下面的示例显示了posflag.Provider的使用,这是一个由SPF13/PFLAG库(高级命令行Lib)上的包装器。对于GO内置的flag软件包,请使用basicflag.Provider 。
koanf/v2\”
\”github.com/knadh/ koanf /parsers/toml\”
// TOML version 2 is available at:
// \”github.com/knadh/ koanf /parsers/toml/v2\”
\”github.com/knadh/ koanf /providers/file\”
\”github.com/knadh/ koanf /providers/posflag\”
flag \”github.com/spf13/pflag\”
)
// Global koanf instance. Use \”.\” as the key path delimiter. This can be \”/\” or any character.
var k = koanf .New(\”.\”)
func main() {
// Use the POSIX compliant pflag lib instead of Go\’s flag lib.
f := flag.NewFlagSet(\”config\”, flag.ContinueOnError)
f.Usage = func() {
fmt.Println(f.FlagUsages())
os.Exit(0)
}
// Path to one or more config files to load into koanf along with some config params.
f.StringSlice(\”conf\”, []string{\”mock/mock.toml\”}, \”path to one or more .toml config files\”)
f.String(\”time\”, \”2020-01-01\”, \”a time string\”)
f.String(\”type\”, \”xxx\”, \”type of the app\”)
f.Parse(os.Args[1:])
// Load the config files provided in the commandline.
cFiles, _ := f.GetStringSlice(\”conf\”)
for _, c := range cFiles {
if err := k.Load(file.Provider(c), toml.Parser()); err != nil {
log.Fatalf(\”error loading file: %v\”, err)
}
}
// \”time\” and \”type\” may have been loaded from the config file, but
// they can still be overridden with the values from the command line.
// The bundled posflag.Provider takes a flagset from the spf13/pflag lib.
// Passing the koanf instance to posflag helps it deal with default command
// line flag values that are not present in conf maps from previously loaded
// providers.
if err := k.Load(posflag.Provider(f, \”.\”, k), nil); err != nil {
log.Fatalf(\”error loading config: %v\”, err)
}
fmt.Println(\”time is = \”, k.String(\”time\”))
}\”>
package main import ( \"fmt\" \"log\" \"os\" \"github.com/knadh/ koanf /v2\" \"github.com/knadh/ koanf /parsers/toml\" // TOML version 2 is available at: // \"github.com/knadh/ koanf /parsers/toml/v2\" \"github.com/knadh/ koanf /providers/file\" \"github.com/knadh/ koanf /providers/posflag\" flag \"github.com/spf13/pflag\" ) // Global koanf instance. Use \".\" as the key path delimiter. This can be \"/\" or any character. var k = koanf . New ( \".\" ) func main () { // Use the POSIX compliant pflag lib instead of Go\'s flag lib. f := flag . NewFlagSet ( \"config\" , flag . ContinueOnError ) f . Usage = func () { fmt . Println ( f . FlagUsages ()) os . Exit ( 0 ) } // Path to one or more config files to load into koanf along with some config params. f . StringSlice ( \"conf\" , [] string { \"mock/mock.toml\" }, \"path to one or more .toml config files\" ) f . String ( \"time\" , \"2020-01-01\" , \"a time string\" ) f . String ( \"type\" , \"xxx\" , \"type of the app\" ) f . Parse ( os . Args [ 1 :]) // Load the config files provided in the commandline. cFiles , _ := f . GetStringSlice ( \"conf\" ) for _ , c := range cFiles { if err := k . Load ( file . Provider ( c ), toml . Parser ()); err != nil { log . Fatalf ( \"error loading file: %v\" , err ) } } // \"time\" and \"type\" may have been loaded from the config file, but // they can still be overridden with the values from the command line. // The bundled posflag.Provider takes a flagset from the spf13/pflag lib. // Passing the koanf instance to posflag helps it deal with default command // line flag values that are not present in conf maps from previously loaded // providers. if err := k . Load ( posflag . Provider ( f , \".\" , k ), nil ); err != nil { log . Fatalf ( \"error loading config: %v\" , err ) } fmt . Println ( \"time is = \" , k . String ( \"time\" )) }
阅读环境变量
koanf/v2\”
\”github.com/knadh/ koanf /parsers/json\”
\”github.com/knadh/ koanf /providers/env/v2\”
\”github.com/knadh/ koanf /providers/file\”
)
// Global koanf instance. Use . as the key path delimiter. This can be / or anything.
var k = koanf .New(\”.\”)
func main() {
// Load JSON config.
if err := k.Load(file.Provider(\”mock/mock.json\”), json.Parser()); err != nil {
log.Fatalf(\”error loading config: %v\”, err)
}
// Load only environment variables with prefix \”MYVAR_\” and merge into config.
// Transform var names by:
// 1. Converting to lowercase
// 2. Removing \”MYVAR_\” prefix
// 3. Replacing \”_\” with \”.\” to representing nesting using the . delimiter.
// Example: MYVAR_PARENT1_CHILD1_NAME becomes \”parent1.child1.name\”
k.Load(env.Provider(\”.\”, env.Opt{
Prefix: \”MYVAR_\”,
TransformFunc: func(k, v string) (string, any) {
// Transform the key.
k = strings.ReplaceAll(strings.ToLower(strings.TrimPrefix(k, \”MYVAR_\”)), \”_\”, \”.\”)
// Transform the value into slices, if they contain spaces.
// Eg: MYVAR_TAGS=\”foo bar baz\” -> tags: [\”foo\”, \”bar\”, \”baz\”]
// This is to demonstrate that string values can be transformed to any type
// where necessary.
if strings.Contains(v, \” \”) {
return k, strings.Split(v, \” \”)
}
return k, v
},
}), nil)
fmt.Println(\”name is =\”, k.String(\”parent1.child1.name\”))
fmt.Println(\”time is =\”, k.Time(\”time\”, time.DateOnly))
fmt.Println(\”ids are =\”, k.Strings(\”parent1.child1.grandchild1.ids\”))
}“`
### Reading from an S3 bucket
“`go
// Load JSON config from s3.
if err := k.Load(s3.Provider(s3.Config{
AccessKey: os.Getenv(\”AWS_S3_ACCESS_KEY\”),
SecretKey: os.Getenv(\”AWS_S3_SECRET_KEY\”),
Region: os.Getenv(\”AWS_S3_REGION\”),
Bucket: os.Getenv(\”AWS_S3_BUCKET\”),
ObjectKey: \”dir/config.json\”,
}), json.Parser()); err != nil {
log.Fatalf(\”error loading config: %v\”, err)
}\”>
package main import ( \"fmt\" \"log\" \"strings\" \"github.com/knadh/ koanf /v2\" \"github.com/knadh/ koanf /parsers/json\" \"github.com/knadh/ koanf /providers/env/v2\" \"github.com/knadh/ koanf /providers/file\" ) // Global koanf instance. Use . as the key path delimiter. This can be / or anything. var k = koanf . New ( \".\" ) func main () { // Load JSON config. if err := k . Load ( file . Provider ( \"mock/mock.json\" ), json . Parser ()); err != nil { log . Fatalf ( \"error loading config: %v\" , err ) } // Load only environment variables with prefix \"MYVAR_\" and merge into config. // Transform var names by: // 1. Converting to lowercase // 2. Removing \"MYVAR_\" prefix // 3. Replacing \"_\" with \".\" to representing nesting using the . delimiter. // Example: MYVAR_PARENT1_CHILD1_NAME becomes \"parent1.child1.name\" k . Load ( env . Provider ( \".\" , env. Opt { Prefix : \"MYVAR_\" , TransformFunc : func ( k , v string ) ( string , any ) { // Transform the key. k = strings . ReplaceAll ( strings . ToLower ( strings . TrimPrefix ( k , \"MYVAR_\" )), \"_\" , \".\" ) // Transform the value into slices, if they contain spaces. // Eg: MYVAR_TAGS=\"foo bar baz\" -> tags: [\"foo\", \"bar\", \"baz\"] // This is to demonstrate that string values can be transformed to any type // where necessary. if strings . Contains ( v , \" \" ) { return k , strings . Split ( v , \" \" ) } return k , v }, }), nil ) fmt . Println ( \"name is =\" , k . String ( \"parent1.child1.name\" )) fmt . Println ( \"time is =\" , k . Time ( \"time\" , time . DateOnly )) fmt . Println ( \"ids are =\" , k . Strings ( \"parent1.child1.grandchild1.ids\" )) } `` ` ### Reading from an S3 bucket ` `` go // Load JSON config from s3. if err := k . Load ( s3 . Provider (s3. Config { AccessKey : os . Getenv ( \"AWS_S3_ACCESS_KEY\" ), SecretKey : os . Getenv ( \"AWS_S3_SECRET_KEY\" ), Region : os . Getenv ( \"AWS_S3_REGION\" ), Bucket : os . Getenv ( \"AWS_S3_BUCKET\" ), ObjectKey : \"dir/config.json\" , }), json . Parser ()); err != nil { log . Fatalf ( \"error loading config: %v\" , err ) }
读取原始字节
捆绑的rawbytes提供商可用于从源中读取任意字节,例如数据库或HTTP调用。
koanf/v2\”
\”github.com/knadh/ koanf /parsers/json\”
\”github.com/knadh/ koanf /providers/rawbytes\”
)
// Global koanf instance. Use . as the key path delimiter. This can be / or anything.
var k = koanf .New(\”.\”)
func main() {
b := []byte(`{\”type\”: \”rawbytes\”, \”parent1\”: {\”child1\”: {\”type\”: \”rawbytes\”}}}`)
k.Load(rawbytes.Provider(b), json.Parser())
fmt.Println(\”type is = \”, k.String(\”parent1.child1.type\”))
}\”>
package main import ( \"fmt\" \"github.com/knadh/ koanf /v2\" \"github.com/knadh/ koanf /parsers/json\" \"github.com/knadh/ koanf /providers/rawbytes\" ) // Global koanf instance. Use . as the key path delimiter. This can be / or anything. var k = koanf . New ( \".\" ) func main () { b := [] byte ( `{\"type\": \"rawbytes\", \"parent1\": {\"child1\": {\"type\": \"rawbytes\"}}}` ) k . Load ( rawbytes . Provider ( b ), json . Parser ()) fmt . Println ( \"type is = \" , k . String ( \"parent1.child1.type\" )) }
解除和编组
Parser可用于根据字段标签将koanf实例中的值和扫描值扫描到一个结构中,并将koanf实例元用回到序列化字节中,例如json或yaml文件
koanf/v2\”
\”github.com/knadh/ koanf /parsers/json\”
\”github.com/knadh/ koanf /providers/file\”
)
// Global koanf instance. Use . as the key path delimiter. This can be / or anything.
var (
k = koanf .New(\”.\”)
parser = json.Parser()
)
func main() {
// Load JSON config.
if err := k.Load(file.Provider(\”mock/mock.json\”), parser); err != nil {
log.Fatalf(\”error loading config: %v\”, err)
}
// Structure to unmarshal nested conf to.
type childStruct struct {
Name string ` koanf :\”name\”`
Type string ` koanf :\”type\”`
Empty map[string]string ` koanf :\”empty\”`
GrandChild struct {
Ids []int ` koanf :\”ids\”`
On bool ` koanf :\”on\”`
} ` koanf :\”grandchild1\”`
}
var out childStruct
// Quick unmarshal.
k.Unmarshal(\”parent1.child1\”, &out)
fmt.Println(out)
// Unmarshal with advanced config.
out = childStruct{}
k.UnmarshalWithConf(\”parent1.child1\”, &out, koanf .UnmarshalConf{Tag: \” koanf \”})
fmt.Println(out)
// Marshal the instance back to JSON.
// The parser instance can be anything, eg: json.Parser(), yaml.Parser() etc.
b, _ := k.Marshal(parser)
fmt.Println(string(b))
}\”>
package main import ( \"fmt\" \"log\" \"github.com/knadh/ koanf /v2\" \"github.com/knadh/ koanf /parsers/json\" \"github.com/knadh/ koanf /providers/file\" ) // Global koanf instance. Use . as the key path delimiter. This can be / or anything. var ( k = koanf . New ( \".\" ) parser = json . Parser () ) func main () { // Load JSON config. if err := k . Load ( file . Provider ( \"mock/mock.json\" ), parser ); err != nil { log . Fatalf ( \"error loading config: %v\" , err ) } // Structure to unmarshal nested conf to. type childStruct struct { Name string ` koanf :\"name\"` Type string ` koanf :\"type\"` Empty map [ string ] string ` koanf :\"empty\"` GrandChild struct { Ids [] int ` koanf :\"ids\"` On bool ` koanf :\"on\"` } ` koanf :\"grandchild1\"` } var out childStruct // Quick unmarshal. k . Unmarshal ( \"parent1.child1\" , & out ) fmt . Println ( out ) // Unmarshal with advanced config. out = childStruct {} k . UnmarshalWithConf ( \"parent1.child1\" , & out , koanf . UnmarshalConf { Tag : \" koanf \" }) fmt . Println ( out ) // Marshal the instance back to JSON. // The parser instance can be anything, eg: json.Parser(), yaml.Parser() etc. b , _ := k . Marshal ( parser ) fmt . Println ( string ( b )) }
用平坦的路径拆开
有时,有必要将各种钥匙从各种嵌套结构转换为平坦的目标结构。 UnmarshalConf.FlatPaths标志是可能的。
koanf/v2\”
\”github.com/knadh/ koanf /parsers/json\”
\”github.com/knadh/ koanf /providers/file\”
)
// Global koanf instance. Use . as the key path delimiter. This can be / or anything.
var k = koanf .New(\”.\”)
func main() {
// Load JSON config.
if err := k.Load(file.Provider(\”mock/mock.json\”), json.Parser()); err != nil {
log.Fatalf(\”error loading config: %v\”, err)
}
type rootFlat struct {
Type string ` koanf :\”type\”`
Empty map[string]string ` koanf :\”empty\”`
Parent1Name string ` koanf :\”parent1.name\”`
Parent1ID int ` koanf :\”parent1.id\”`
Parent1Child1Name string ` koanf :\”parent1.child1.name\”`
Parent1Child1Type string ` koanf :\”parent1.child1.type\”`
Parent1Child1Empty map[string]string ` koanf :\”parent1.child1.empty\”`
Parent1Child1Grandchild1IDs []int ` koanf :\”parent1.child1.grandchild1.ids\”`
Parent1Child1Grandchild1On bool ` koanf :\”parent1.child1.grandchild1.on\”`
}
// Unmarshal the whole root with FlatPaths: True.
var o1 rootFlat
k.UnmarshalWithConf(\”\”, &o1, koanf .UnmarshalConf{Tag: \” koanf \”, FlatPaths: true})
fmt.Println(o1)
// Unmarshal a child structure of \”parent1\”.
type subFlat struct {
Name string ` koanf :\”name\”`
ID int ` koanf :\”id\”`
Child1Name string ` koanf :\”child1.name\”`
Child1Type string ` koanf :\”child1.type\”`
Child1Empty map[string]string ` koanf :\”child1.empty\”`
Child1Grandchild1IDs []int ` koanf :\”child1.grandchild1.ids\”`
Child1Grandchild1On bool ` koanf :\”child1.grandchild1.on\”`
}
var o2 subFlat
k.UnmarshalWithConf(\”parent1\”, &o2, koanf .UnmarshalConf{Tag: \” koanf \”, FlatPaths: true})
fmt.Println(o2)
}\”>
package main import ( \"fmt\" \"log\" \"github.com/knadh/ koanf /v2\" \"github.com/knadh/ koanf /parsers/json\" \"github.com/knadh/ koanf /providers/file\" ) // Global koanf instance. Use . as the key path delimiter. This can be / or anything. var k = koanf . New ( \".\" ) func main () { // Load JSON config. if err := k . Load ( file . Provider ( \"mock/mock.json\" ), json . Parser ()); err != nil { log . Fatalf ( \"error loading config: %v\" , err ) } type rootFlat struct { Type string ` koanf :\"type\"` Empty map [ string ] string ` koanf :\"empty\"` Parent1Name string ` koanf :\"parent1.name\"` Parent1ID int ` koanf :\"parent1.id\"` Parent1Child1Name string ` koanf :\"parent1.child1.name\"` Parent1Child1Type string ` koanf :\"parent1.child1.type\"` Parent1Child1Empty map [ string ] string ` koanf :\"parent1.child1.empty\"` Parent1Child1Grandchild1IDs [] int ` koanf :\"parent1.child1.grandchild1.ids\"` Parent1Child1Grandchild1On bool ` koanf :\"parent1.child1.grandchild1.on\"` } // Unmarshal the whole root with FlatPaths: True. var o1 rootFlat k . UnmarshalWithConf ( \"\" , & o1 , koanf . UnmarshalConf { Tag : \" koanf \" , FlatPaths : true }) fmt . Println ( o1 ) // Unmarshal a child structure of \"parent1\". type subFlat struct { Name string ` koanf :\"name\"` ID int ` koanf :\"id\"` Child1Name string ` koanf :\"child1.name\"` Child1Type string ` koanf :\"child1.type\"` Child1Empty map [ string ] string ` koanf :\"child1.empty\"` Child1Grandchild1IDs [] int ` koanf :\"child1.grandchild1.ids\"` Child1Grandchild1On bool ` koanf :\"child1.grandchild1.on\"` } var o2 subFlat k . UnmarshalWithConf ( \"parent1\" , & o2 , koanf . UnmarshalConf { Tag : \" koanf \" , FlatPaths : true }) fmt . Println ( o2 ) }
从嵌套地图上读取
捆绑的confmap提供商采用可以加载到koanf实例中的map[string]interface{} 。
koanf/v2\”
\”github.com/knadh/ koanf /providers/confmap\”
\”github.com/knadh/ koanf /providers/file\”
\”github.com/knadh/ koanf /parsers/json\”
\”github.com/knadh/ koanf /parsers/yaml\”
)
// Global koanf instance. Use \”.\” as the key path delimiter. This can be \”/\” or any character.
var k = koanf .New(\”.\”)
func main() {
// Load default values using the confmap provider.
// We provide a flat map with the \”.\” delimiter.
// A nested map can be loaded by setting the delimiter to an empty string \”\”.
k.Load(confmap.Provider(map[string]interface{}{
\”parent1.name\”: \”Default Name\”,
\”parent3.name\”: \”New name here\”,
}, \”.\”), nil)
// Load JSON config on top of the default values.
if err := k.Load(file.Provider(\”mock/mock.json\”), json.Parser()); err != nil {
log.Fatalf(\”error loading config: %v\”, err)
}
// Load YAML config and merge into the previously loaded config (because we can).
k.Load(file.Provider(\”mock/mock.yml\”), yaml.Parser())
fmt.Println(\”parent\’s name is = \”, k.String(\”parent1.name\”))
fmt.Println(\”parent\’s ID is = \”, k.Int(\”parent1.id\”))
}\”>
package main import ( \"fmt\" \"log\" \"github.com/knadh/ koanf /v2\" \"github.com/knadh/ koanf /providers/confmap\" \"github.com/knadh/ koanf /providers/file\" \"github.com/knadh/ koanf /parsers/json\" \"github.com/knadh/ koanf /parsers/yaml\" ) // Global koanf instance. Use \".\" as the key path delimiter. This can be \"/\" or any character. var k = koanf . New ( \".\" ) func main () { // Load default values using the confmap provider. // We provide a flat map with the \".\" delimiter. // A nested map can be loaded by setting the delimiter to an empty string \"\". k . Load ( confmap . Provider ( map [ string ] interface {}{ \"parent1.name\" : \"Default Name\" , \"parent3.name\" : \"New name here\" , }, \".\" ), nil ) // Load JSON config on top of the default values. if err := k . Load ( file . Provider ( \"mock/mock.json\" ), json . Parser ()); err != nil { log . Fatalf ( \"error loading config: %v\" , err ) } // Load YAML config and merge into the previously loaded config (because we can). k . Load ( file . Provider ( \"mock/mock.yml\" ), yaml . Parser ()) fmt . Println ( \"parent\'s name is = \" , k . String ( \"parent1.name\" )) fmt . Println ( \"parent\'s ID is = \" , k . Int ( \"parent1.id\" )) }
从结构阅读
捆绑的structs提供商可用于读取从结构的数据,以加载到koanf实例中。
koanf/v2\”
\”github.com/knadh/ koanf /providers/structs\”
)
// Global koanf instance. Use \”.\” as the key path delimiter. This can be \”/\” or any character.
var k = koanf .New(\”.\”)
type parentStruct struct {
Name string ` koanf :\”name\”`
ID int ` koanf :\”id\”`
Child1 childStruct ` koanf :\”child1\”`
}
type childStruct struct {
Name string ` koanf :\”name\”`
Type string ` koanf :\”type\”`
Empty map[string]string ` koanf :\”empty\”`
Grandchild1 grandchildStruct ` koanf :\”grandchild1\”`
}
type grandchildStruct struct {
Ids []int ` koanf :\”ids\”`
On bool ` koanf :\”on\”`
}
type sampleStruct struct {
Type string ` koanf :\”type\”`
Empty map[string]string ` koanf :\”empty\”`
Parent1 parentStruct ` koanf :\”parent1\”`
}
func main() {
// Load default values using the structs provider.
// We provide a struct along with the struct tag ` koanf ` to the
// provider.
k.Load(structs.Provider(sampleStruct{
Type: \”json\”,
Empty: make(map[string]string),
Parent1: parentStruct{
Name: \”parent1\”,
ID: 1234,
Child1: childStruct{
Name: \”child1\”,
Type: \”json\”,
Empty: make(map[string]string),
Grandchild1: grandchildStruct{
Ids: []int{1, 2, 3},
On: true,
},
},
},
}, \” koanf \”), nil)
fmt.Printf(\”name is = `%s`\\n\”, k.String(\”parent1.child1.name\”))
}\”>
package main import ( \"fmt\" \"github.com/knadh/ koanf /v2\" \"github.com/knadh/ koanf /providers/structs\" ) // Global koanf instance. Use \".\" as the key path delimiter. This can be \"/\" or any character. var k = koanf . New ( \".\" ) type parentStruct struct { Name string ` koanf :\"name\"` ID int ` koanf :\"id\"` Child1 childStruct ` koanf :\"child1\"` } type childStruct struct { Name string ` koanf :\"name\"` Type string ` koanf :\"type\"` Empty map [ string ] string ` koanf :\"empty\"` Grandchild1 grandchildStruct ` koanf :\"grandchild1\"` } type grandchildStruct struct { Ids [] int ` koanf :\"ids\"` On bool ` koanf :\"on\"` } type sampleStruct struct { Type string ` koanf :\"type\"` Empty map [ string ] string ` koanf :\"empty\"` Parent1 parentStruct ` koanf :\"parent1\"` } func main () { // Load default values using the structs provider. // We provide a struct along with the struct tag ` koanf ` to the // provider. k . Load ( structs . Provider ( sampleStruct { Type : \"json\" , Empty : make ( map [ string ] string ), Parent1 : parentStruct { Name : \"parent1\" , ID : 1234 , Child1 : childStruct { Name : \"child1\" , Type : \"json\" , Empty : make ( map [ string ] string ), Grandchild1 : grandchildStruct { Ids : [] int { 1 , 2 , 3 }, On : true , }, }, }, }, \" koanf \" ), nil ) fmt . Printf ( \"name is = `%s` \\n \" , k . String ( \"parent1.child1.name\" )) }
合并行为
默认行为
当您以这种方式创建koanf时,默认行为是: koanf .New(delim) ,最新的加载配置将与上一个合并。
例如: first.yml
key : [1,2,3]
second.yml
key : \' string \'
加载second.yml时,它将覆盖first.yml类型。
如果不需要此行为,则可以“严格”合并。在同一情况下, Load将返回错误。
koanf/v2\”
\”github.com/knadh/ koanf /maps\”
\”github.com/knadh/ koanf /parsers/json\”
\”github.com/knadh/ koanf /parsers/yaml\”
\”github.com/knadh/ koanf /providers/file\”
)
var conf = koanf .Conf{
Delim: \”.\”,
StrictMerge: true,
}
var k = koanf .NewWithConf(conf)
func main() {
yamlPath := \”mock/mock.yml\”
if err := k.Load(file.Provider(yamlPath), yaml.Parser()); err != nil {
log.Fatalf(\”error loading config: %v\”, err)
}
jsonPath := \”mock/mock.json\”
if err := k.Load(file.Provider(jsonPath), json.Parser()); err != nil {
log.Fatalf(\”error loading config: %v\”, err)
}
}\”>
package main import ( \"errors\" \"log\" \"github.com/knadh/ koanf /v2\" \"github.com/knadh/ koanf /maps\" \"github.com/knadh/ koanf /parsers/json\" \"github.com/knadh/ koanf /parsers/yaml\" \"github.com/knadh/ koanf /providers/file\" ) var conf = koanf . Conf { Delim : \".\" , StrictMerge : true , } var k = koanf . NewWithConf ( conf ) func main () { yamlPath := \"mock/mock.yml\" if err := k . Load ( file . Provider ( yamlPath ), yaml . Parser ()); err != nil { log . Fatalf ( \"error loading config: %v\" , err ) } jsonPath := \"mock/mock.json\" if err := k . Load ( file . Provider ( jsonPath ), json . Parser ()); err != nil { log . Fatalf ( \"error loading config: %v\" , err ) } }
注意:当合并不同的扩展名时,每个解析器都可以对其类型的处理方式有所不同,这意味着即使您的负载相同类型,也可能会在StrictMerge: true 。
例如:合并JSON和YAML很可能会失败,因为JSON将整数视为Float64,而Yaml将其视为整数。
合并和关键案例灵敏度的顺序
- config键在koanf中对大小写。例如,
app.server.port和APP.SERVER.port不一样。 - koanf不会在来自各个提供商的加载配置上施加任何订购。每个连续的
Load()或Merge()将新的配置合并到现有配置中。也就是说,可以先加载环境变量,然后在其顶部加载文件,然后在其顶部或任何此类订单上加载命令行变量。
定制提供商和解析器
提供商返回一个可以直接加载到koanf koanf .Load()中的嵌套map[string]interface{}配置,或者它可以返回可以用解析器解析的原始字节(再次使用koanf .Load()加载。
定制合并策略
默认情况下,当使用Load()合并两个配置源时, koanf递归合并了嵌套地图( map[string]interface{} )的键,而静态值则被覆盖(切片,字符串等)。可以通过使用WithMergeFunc选项提供自定义合并函数来更改此行为。
koanf/v2\”
\”github.com/knadh/ koanf /maps\”
\”github.com/knadh/ koanf /parsers/json\”
\”github.com/knadh/ koanf /parsers/yaml\”
\”github.com/knadh/ koanf /providers/file\”
)
var conf = koanf .Conf{
Delim: \”.\”,
S
