我刚开始学习Go,就遇到了这个问题。我正在制作一个 YAML 解析器来填充结构,然后替换默认值。
import (
"os"
"github.com/creasty/defaults"
"gopkg.in/yaml.v3"
)
type ConfigSchema struct {
Metadata MetadataSchema `yaml:"metadata"`
Fields []FieldSchema `yaml:"fields"`
}
type MetadataSchema struct {
Name string `yaml:"name"`
}
type FieldSchema struct {
Settings FieldSettingsSchema `yaml:"settings"`
Value string `yaml:"value"`
}
type FieldSettingsSchema struct {
Description string `yaml:"description"`
Enabled bool `yaml:"enabled" default:"true"`
}
func main() {
yamlPath := "C:/path/test.yml"
var configSchema ConfigSchema
_ = parse(yamlPath, configSchema)
}
func parse(filePath string, out interface{}) error {
fileContent, err := os.ReadFile(filePath)
if err != nil {
return err
}
err = yaml.Unmarshal(fileContent, &out)
if err != nil {
return err
}
err = defaults.Set(out) // <- "not a struct pointer"
if err != nil {
panic(err)
}
return nil
}
文件解析正确,但由于某种原因,在填写空值时出现错误not a struct pointer
。你能告诉我如何修复它吗?
我想也许我还应该指出 中的链接defaults.Set(&out)
,但错误仍然存在。
另一种选择是将初始化放在var configSchema ConfigSchema
函数中parse
并返回(ConfigSchema, err)
。在这种情况下,一切正常,但有一个特定结构的绑定,但我想实现一个更通用的解析器。
看看出了什么问题。
如何
defaults.Set
检查内容视图:reflect.Struct
当您调用代码时
parse(yamlPath, configSchema)
,会在堆栈上创建该结构的副本configSchema
,并将指向该副本的指针打包到其中interface{}
。当通过方法解压该容器时,reflect
内容类型将为ConfigSchema
。当您将其传递out
给时defaults.Set
,他们将检查容器内容的类型interface{}
- 结果是Struct
,不是Ptr
。因此出现了错误。为什么它在你的情况下不起作用
defaults.Set(&out)
?原因是defaults.Set
它将接收类型为 的对象*interface{}
,而不是*ConfigSchema
:由于 Go 的静态类型,仅存储程序文本中给出的类型信息。如果out
它有类型interface{}
,那么&out
它就有类型*interface{}
。defaults.Set
将在第二阶段被拒绝*interface{}
:指针指向 形式的对象Interface
,而不是Struct
。这是一个
defaults.Set
他不发誓的例子。你看到区别了吗?在内部,
parse
我只使用out
,并且不使用指向该接口的指针。指针必须直接传递给函数。我将
parse
指向结果的指针传递给函数:err = parse(bz, &cfg)
。指向指针的指针被打包到容器中,解包时interface{}
获得 类型的对象*ConfigSchema
。然后我传入defaults.Set
对象本身out
,其中包含指向该结构的指针。因此,defaults.Set
它可以准确找到指向该结构的指针,并且不会返回错误。