koanf

Delphi源码 2025-08-16

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.portAPP.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
下载源码

通过命令行克隆项目:

git clone https://github.com/knadh/koanf.git