gpt4 book ai didi

go - 为什么当我通过反射构造它时,Golang yaml.v2 将我的结构转换为映射?

转载 作者:IT王子 更新时间:2023-10-29 02:01:36 25 4
gpt4 key购买 nike

我正在开发一个通用配置解析器,它读取 YAML 配置文件并将结果存储在结构中。我希望解析器是类型不可知的,并且我想实现一些覆盖逻辑,所以我正在使用反射。

下面是我正在处理的一个完整但非常简化的版本,它说明了围绕调用 yaml.Unmarshal 的问题。如果我传入一个指向我在没有反射的情况下创建的结构的指针(示例代码中的 base2 := TestConf{}),它会按预期工作:一个强类型的结构进入,一个强类型的结构进入类型结构出来了。

但是,如果我传入一个我用反射创建的结构(base := reflect.New(configType).Elem().Interface() 在示例代码中),我传入一个结构并返回一个 map[interface{}]interface{}。如您所见,我已尽最大努力验证这两个结构是否相同,如果它们的类型不同或者它们不是 DeepEqual,我会感到 panic 。

目前这让我很头疼,但我可以解决它。我只是想了解为什么会发生这种情况,或许还想了解一种解决方法。

package main

import (
"fmt"
"io/ioutil"
"os"
"reflect"
"time"

yaml "gopkg.in/yaml.v2"
)

type TestConf struct {
RequiredConfig `yaml:"RequiredConfig"`
Str1 string `yaml:"Str1"`
Strptr1 *string `yaml:"Strptr1"`
TimePtr *time.Time `yaml:"TimePtr"`
}

type RequiredConfig struct {
Environment string `yaml:"Environment"`
}

var BaseConfigPath = "./config_test.yml"

func main() {
conf := TestConf{}
LoadConfig(&conf)
}

func LoadConfig(target interface{}) {
targetActual := reflect.ValueOf(target).Elem()
configType := targetActual.Type()
base := reflect.New(configType).Elem().Interface()
base2 := TestConf{}

if reflect.TypeOf(base) != reflect.TypeOf(base2) {
panic("your argument is invalid")
}

if !reflect.DeepEqual(base, base2) {
panic("your argument is invalid")
}

if _, err := os.Stat(BaseConfigPath); !os.IsNotExist(err) {
raw, _ := ioutil.ReadFile(BaseConfigPath)

fmt.Printf("Before base Type: \"%v\", Kind: \"%v\"\n", reflect.TypeOf(base), reflect.ValueOf(base).Kind())
err = yaml.Unmarshal(raw, &base)
fmt.Printf("After base Type: \"%v\", Kind: \"%v\"\n", reflect.TypeOf(base), reflect.ValueOf(base).Kind())

fmt.Printf("Before base2 Type: \"%v\", Kind: \"%v\"\n", reflect.TypeOf(base2), reflect.ValueOf(base2).Kind())
err = yaml.Unmarshal(raw, &base2)
fmt.Printf("After base2 Type: \"%v\", Kind: \"%v\"\n", reflect.TypeOf(base2), reflect.ValueOf(base2).Kind())
}
}

要运行它,您还需要保存在 ./config_test.yml 的 YAML 文件:

RequiredConfig:
Environment: dev
Str1: String 1
Strptr1: String pointer 1
TimePtr: 2018-08-01T17:25:50.179949-04:00

我得到的输出:

Before base Type: "main.TestConf", Kind: "struct"
After base Type: "map[interface {}]interface {}", Kind: "map"
Before base2 Type: "main.TestConf", Kind: "struct"
After base2 Type: "main.TestConf", Kind: "struct"

因此 base2 按预期运行。 base 以某种方式转换为 map 。

最佳答案

那是因为这一行:

base := reflect.New(configType).Elem().Interface()

此时是字面上的类型接口(interface){}。

如果你这样做:

base, ok := base := reflect.New(configType).Elem().Interface().(TestConf)
if !ok{
panic("got wrong type")
}
//rest of your code

你会得到你期望的结果。

编辑地址回复

传递指针接口(interface)封装

package main

import (
"fmt"
"io/ioutil"
"os"
"reflect"
"time"

yaml "gopkg.in/yaml.v2"
)

type TestConf struct {
RequiredConfig `yaml:"RequiredConfig"`
Str1 string `yaml:"Str1"`
Strptr1 *string `yaml:"Strptr1"`
TimePtr *time.Time `yaml:"TimePtr"`
}

type RequiredConfig struct {
Environment string `yaml:"Environment"`
}

var BaseConfigPath = "./config_test.yml"

func main() {
conf := TestConf{}
LoadConfig(&conf)
}

func LoadConfig(target interface{}) {
targetActual := reflect.ValueOf(target).Elem()
configType := targetActual.Type()
baseReflect := reflect.New(configType)
// Actual type.
base := baseReflect.Elem().Interface()
base2 := TestConf{}

if reflect.TypeOf(base) != reflect.TypeOf(base2) {
panic("your argument is invalid")
}

if !reflect.DeepEqual(base, base2) {
panic("your argument is invalid")
}

if _, err := os.Stat(BaseConfigPath); !os.IsNotExist(err) {
raw, _ := ioutil.ReadFile(BaseConfigPath)

fmt.Printf("Before base Type: \"%v\", Kind: \"%v\"\n", reflect.TypeOf(base), reflect.ValueOf(base).Kind())
// Passes the pointer to unmarshal
err = yaml.Unmarshal(raw, baseReflect.Interface())
fmt.Printf("After base Type: \"%v\", Kind: \"%v\"\n", reflect.TypeOf(base), reflect.ValueOf(base).Kind())

fmt.Printf("Before base2 Type: \"%v\", Kind: \"%v\"\n", reflect.TypeOf(base2), reflect.ValueOf(base2).Kind())
err = yaml.Unmarshal(raw, &base2)
fmt.Printf("After base2 Type: \"%v\", Kind: \"%v\"\n", reflect.TypeOf(base2), reflect.ValueOf(base2).Kind())
}
}

产量:

Before base Type: "main.TestConf", Kind: "struct"
After base Type: "main.TestConf", Kind: "struct"
Before base2 Type: "main.TestConf", Kind: "struct"
After base2 Type: "main.TestConf", Kind: "struct"

关于go - 为什么当我通过反射构造它时,Golang yaml.v2 将我的结构转换为映射?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51656182/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com