侧边栏壁纸
博主头像
分享你我博主等级

行动起来,活在当下

  • 累计撰写 112 篇文章
  • 累计创建 13 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

golang使用koanf读取yaml文件支持读取默认值

管理员
2024-12-23 / 0 评论 / 0 点赞 / 8 阅读 / 8968 字
// Cfg 是全局配置
var Cfg *models.Config

func InitConfig() {
	// 创建 Koanf 实例
	k := koanf.New(".")
	proxy := file.Provider("conf/proxy.yaml")
	// 加载第一个配置文件
	if err := k.Load(proxy, yaml.Parser()); err != nil {
		log.Fatalf("Error loading config file 'proxy.yaml': %v", err)
	}

	// 加载第二个配置文件并合并
	if err := k.Load(file.Provider("conf/backup_upstream.yaml"), yaml.Parser()); err != nil {
		log.Fatalf("Error loading config file 'backup_upstream.yaml': %v", err)
	}

	// 将配置解码到结构体中
	if err := k.Unmarshal("", &Cfg); err != nil {
		log.Fatalf("Unable to unmarshal into struct: %v", err)
	}
	setDefaults(Cfg)
	fmt.Println("配置文件加载成功")
}

// 从结构体标签中提取默认值并设置到 koanf 中
func setDefaults(v interface{}, k *koanf.Koanf, parentPath string, isNil bool) {
	if v == nil {
		return
	}
	val := reflect.ValueOf(v).Elem() // 获取指向结构体的指针
	if !val.IsValid() {
		// 处理无效值的情况
		return
	}
	typ := val.Type()

	// 遍历结构体的每个字段
	for i := 0; i < val.NumField(); i++ {
		field := val.Field(i)
		fieldType := typ.Field(i)

		// 获取字段标签中的默认值
		defaultValue := fieldType.Tag.Get("default")
		yamlPath := fieldType.Tag.Get("yaml")

		// 如果字段不可设置 (unexported field), 跳过它
		if !field.CanSet() || yamlPath == "" {
			continue
		}
		// Construct the full key path (e.g., "log.pull.enable")
		fullPath := ""
		if parentPath != "" {
			fullPath = parentPath + "." + yamlPath
		} else {
			fullPath = yamlPath
		}
		// 如果是嵌套结构体
		if field.Kind() == reflect.Struct {
			// 递归调用 setDefaults 以设置嵌套结构体的默认值
			setDefaults(field.Addr().Interface(), k, fullPath, isNil)
		} else if field.Kind() == reflect.Slice {
			// 如果字段是 nil,初始化为一个空的切片
			if field.IsNil() && isNil {
				newSlice := reflect.MakeSlice(field.Type(), 0, 0)
				field.Set(newSlice)
			}

			// 获取切片的当前长度
			sliceLen := field.Len()

			// 如果切片为空并且存在默认值,插入默认值
			defaultValue := fieldType.Tag.Get("default")
			if sliceLen == 0 && defaultValue != "" {
				// 为切片元素类型实例化一个元素
				sliceElem := reflect.New(field.Type().Elem()).Elem()

				// 根据字段的默认值类型设置元素值
				if sliceElem.Kind() == reflect.String {
					sliceElem.SetString(defaultValue)
				} else if sliceElem.Kind() == reflect.Int {
					defaultValueInt, err := strconv.Atoi(defaultValue)
					if err != nil {
						log.Fatalf("Failed to parse default value for %s: %v", yamlPath, err)
					}
					sliceElem.SetInt(int64(defaultValueInt))
				} else if sliceElem.Kind() == reflect.Bool {
					defaultValueBool, err := strconv.ParseBool(defaultValue)
					if err != nil {
						log.Fatalf("Failed to parse default value for %s: %v", yamlPath, err)
					}
					sliceElem.SetBool(defaultValueBool)
				}

				// 将新元素追加到切片中
				field.Set(reflect.Append(field, sliceElem))
			}

			// 现在可以安全地访问切片元素
			for i := 0; i < field.Len(); i++ {
				sliceElem := field.Index(i)

				// 如果元素是结构体,递归设置它的默认值
				if sliceElem.Kind() == reflect.Struct {
					setDefaults(sliceElem.Addr().Interface(), k, fullPath, isNil)
				} else if sliceElem.Kind() == reflect.Ptr {
					// 如果切片元素是指针类型并且为 nil,实例化为新的指针
					if sliceElem.IsNil() && isNil {
						newPtr := reflect.New(sliceElem.Type().Elem())
						sliceElem.Set(newPtr)

						// 如果指针指向结构体或 map,递归设置默认值
						if newPtr.Elem().Kind() == reflect.Struct {
							setDefaults(newPtr.Interface(), k, fullPath, isNil)
						} else if newPtr.Elem().Kind() == reflect.Map {
							if newPtr.Elem().IsNil() {
								newPtr.Elem().Set(reflect.MakeMap(newPtr.Elem().Type()))
							}
						}
					} else {
						// 如果指针不为 nil,递归设置它指向的值
						setDefaults(sliceElem.Interface(), k, fullPath, isNil)
					}
				}
			}
		} else if field.Kind() == reflect.Ptr {
			// 如果是指针类型,检查是否为 nil 并且是结构体类型
			if field.IsNil() && isNil {
				// 如果字段为空,实例化为默认的零值
				newPtr := reflect.New(field.Type().Elem())
				field.Set(newPtr)

				// Check if the newly created pointer is a struct or map, and instantiate if needed
				if newPtr.Elem().Kind() == reflect.Struct {
					setDefaults(newPtr.Interface(), k, fullPath, isNil) // Recursively set defaults for the nested struct
				} else if newPtr.Elem().Kind() == reflect.Map {
					if newPtr.Elem().IsNil() {
						newPtr.Elem().Set(reflect.MakeMap(newPtr.Elem().Type()))
					}
				}
			} else {
				// If pointer is not nil, recursively set defaults for the value it points to
				setDefaults(field.Interface(), k, fullPath, isNil)
			}
		} else if field.Kind() == reflect.Map {
			// 如果是 map 类型 (例如 Upstreams),检查是否为 nil 并实例化
			if field.IsNil() && isNil {
				newMap := reflect.MakeMap(field.Type())
				// 实例化为一个空的 map
				field.Set(newMap)
			}
			// 遍历 map 的值
			for _, key := range field.MapKeys() {
				mapValue := field.MapIndex(key)
				if mapValue.Kind() == reflect.Ptr && !mapValue.IsNil() {
					// 对 map 中的每个指针值递归调用 setDefaults
					setDefaults(mapValue.Interface(), k, fullPath, isNil)
				}
			}
		} else if defaultValue != "" && field.IsZero() && !k.Exists(fullPath) {
			// 仅当字段是零值时,才设置默认值
			if field.Kind() == reflect.String {
				field.SetString(defaultValue)
			} else if field.Kind() == reflect.Int {
				defaultValueInt, err := strconv.Atoi(defaultValue)
				if err != nil {
					log.Fatalf("Failed to parse default value for %s: %v", yamlPath, err)
				}
				field.SetInt(int64(defaultValueInt))
			} else if field.Kind() == reflect.Bool {
				defaultValueBool, err := strconv.ParseBool(defaultValue)
				if err != nil {
					log.Fatalf("Failed to parse default value for %s: %v", yamlPath, err)
				}
				field.SetBool(defaultValueBool)
			}
		}
	}
}

根据koanf项判断key项不存在再使用默认值

func setDefaults(v interface{}, k *koanf.Koanf, parentPath string) {
	if v == nil {
		return
	}
	val := reflect.ValueOf(v).Elem() // 获取指向结构体的指针
	typ := val.Type()

	// 遍历结构体的每个字段
	for i := 0; i < val.NumField(); i++ {
		field := val.Field(i)
		fieldType := typ.Field(i)

		// 获取字段标签中的默认值
		defaultValue := fieldType.Tag.Get("default")
		yamlPath := fieldType.Tag.Get("yaml")

		// 如果字段不可设置 (unexported field), 跳过它
		if !field.CanSet() || yamlPath == "" {
			continue
		}
		// Construct the full key path (e.g., "log.pull.enable")
		fullPath := ""
		if parentPath != "" {
			fullPath = parentPath + "." + yamlPath
		} else {
			fullPath = yamlPath
		}
		// 如果是嵌套结构体
		if field.Kind() == reflect.Struct {
			// 递归调用 setDefaults 以设置嵌套结构体的默认值
			setDefaults(field.Addr().Interface(), k, fullPath)
		} else if field.Kind() == reflect.Slice {
			// 如果是切片类型,检查是否为 nil 并实例化
			if field.IsNil() {
				// 如果字段为空,实例化为一个空的切片
				newSlice := reflect.MakeSlice(field.Type(), 0, 0)
				field.Set(newSlice)
			}
		} else if field.Kind() == reflect.Ptr {
			// 如果是指针类型,检查是否为 nil 并且是结构体类型
			if field.IsNil() {
				// 如果字段为空,实例化为默认的零值
				newPtr := reflect.New(field.Type().Elem())
				field.Set(newPtr)

				// Check if the newly created pointer is a struct or map, and instantiate if needed
				if newPtr.Elem().Kind() == reflect.Struct {
					setDefaults(newPtr.Interface(), k, fullPath) // Recursively set defaults for the nested struct
				} else if newPtr.Elem().Kind() == reflect.Map {
					if newPtr.Elem().IsNil() {
						newPtr.Elem().Set(reflect.MakeMap(newPtr.Elem().Type()))
					}
				}
			} else {
				// If pointer is not nil, recursively set defaults for the value it points to
				setDefaults(field.Interface(), k, fullPath)
			}
		} else if field.Kind() == reflect.Map {
			// 如果是 map 类型 (例如 Upstreams),检查是否为 nil 并实例化
			if field.IsNil() {
				newMap := reflect.MakeMap(field.Type())
				// 实例化为一个空的 map
				field.Set(newMap)
			}
			// 遍历 map 的值
			for _, key := range field.MapKeys() {
				mapValue := field.MapIndex(key)
				if mapValue.Kind() == reflect.Ptr && !mapValue.IsNil() {
					// 对 map 中的每个指针值递归调用 setDefaults
					setDefaults(mapValue.Interface(), k, fullPath)
				}
			}
		} else if defaultValue != "" && field.IsZero() && !k.Exists(fullPath) {
			fmt.Println(fullPath)
			// 仅当字段是零值时,才设置默认值
			if field.Kind() == reflect.String {
				field.SetString(defaultValue)
			} else if field.Kind() == reflect.Int {
				defaultValueInt, err := strconv.Atoi(defaultValue)
				if err != nil {
					log.Fatalf("Failed to parse default value for %s: %v", yamlPath, err)
				}
				field.SetInt(int64(defaultValueInt))
			} else if field.Kind() == reflect.Bool {
				defaultValueBool, err := strconv.ParseBool(defaultValue)
				if err != nil {
					log.Fatalf("Failed to parse default value for %s: %v", yamlPath, err)
				}
				field.SetBool(defaultValueBool)
			}
		}
	}
}

结构体

// Config 结构体定义配置数据的结构
type Config struct {
    Logs      *LogsConfig             `yaml:"log"`
}

type LogsConfig struct {
	Pull    *LogsInfoConfig `yaml:"pull"`
	Request *LogsInfoConfig `yaml:"request"`
}

type LogsInfoConfig struct {
	Enable bool `yaml:"enable" default:"false"`
	Info   bool `yaml:"info" default:"false"`
	Debug  bool `yaml:"debug" default:"false"`
	Error  bool `yaml:"error" default:"false"`
}

0

评论区