我正在研究一个通用配置解析器,该解析器读取YAML配置文件并将结果存储在结构中。我希望解析器的类型不可知,并且我想实现一些替代逻辑,所以我使用反射。
下面是我正在研究的完整但非常简化的版本,它说明了调用yaml.Unmarshal时遇到的问题。如果我传递一个指向我创建的没有反射的结构的指针(示例代码中的base2 := TestConf{}
),则它会按预期工作:强类型结构进入,强类型结构出现。
但是,如果传入通过反射创建的结构(示例代码中的base := reflect.New(configType).Elem().Interface()
),则会传入一个结构并返回map[interface{}]interface{}
。如您所见,我已尽力验证这两个结构是否相同,如果它们的类型不同或不是DeepEqual,则会感到恐慌。
目前,这让我头疼不已,但我可以解决它。我只想了解为什么会发生这种情况,也许想想办法解决。
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())
}
}
要运行此文件,您还需要将此YAML文件保存在./config_test.yml
:
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
以某种方式转换为地图。
答案 0 :(得分:2)
那是因为这一行:
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
您将获得预期的结果。
编辑地址回复
传递指针界面变元
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"