我刚开始学习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它可以准确找到指向该结构的指针,并且不会返回错误。