1 回答
TA贡献1877条经验 获得超6个赞
你对这个问题是正确的,它被自动识别为一个map[string]interface{},因为你没有提供自定义 UnmarshalYAML func YAML 包只能这样做。但是您实际上不希望它只是interface{},您需要确定您想要的实际实现。
使用 yaml.v3 的解决方案
UnmarshalYAML如果不提供自定义函数来键入,我看不出如何解决它NodeConfig。如果那是 JSON,我会读作Configa json.RawMessage,然后对于每种可能的类型,我会将其解组为所需的类型,而 yaml.v3 等效项似乎是yaml.Node 类型。
使用它,您可以创建一个类似于NodeConfig具有Configas 的结构yaml.Node,并根据Type值将其转换为具体类型,如下所示:
func (nc *NodeConfig) UnmarshalYAML(value *yaml.Node) error {
var ncu struct {
Type string `yaml:"type"`
Config yaml.Node `yaml:"config"`
}
var err error
// unmarshall into a NodeConfigUnmarshaler to detect correct type
err = value.Decode(&ncu)
if err != nil {
return err
}
// now, detect the type and covert it accordingly
nc.Type = ncu.Type
switch ncu.Type {
case "request":
nc.Config = &RequestConfig{}
case "log":
nc.Config = &LogConfig{}
default:
return fmt.Errorf("unknown type %q", ncu.Type)
}
err = ncu.Config.Decode(nc.Config)
return err
}
示例代码
为了测试这一点,我创建了假人和RequestConfig一个LogConfig样本:
type RequestConfig struct {
Foo string `yaml:"foo"`
Bar string `yaml:"bar"`
}
type LogConfig struct {
Message string `yaml:"message"`
}
func main() {
logSampleYAML := []byte(`
type: log
config:
message: this is a log message
`)
reqSampleYAML := []byte(`
type: request
config:
foo: foo value
bar: bar value
`)
for i, val := range [][]byte{logSampleYAML, reqSampleYAML} {
var nc NodeConfig
err := yaml.Unmarshal(val, &nc)
if err != nil {
fmt.Printf("failed to parse sample %d: %v\n", i, err)
} else {
fmt.Printf("sample %d type %q (%T) = %+v\n", i, nc.Type, nc.Config, nc.Config)
}
}
}
哪些输出:
sample 0 type "log" (*main.LogConfig) = &{Message:this is a log message}
sample 1 type "request" (*main.RequestConfig) = &{Foo:foo value Bar:bar value}
因此,正如您所看到的,每个实例都使用所需的具体类型实例NodeConfig化了,这意味着您现在可以将类型断言用作or (当然是or )。ConfigConfg.(*RequestConfig)Config.(*LogConfig)switch
您可以在这个 Go Playground 完整示例中使用该解决方案。
使用 yaml.v2 的解决方案
我犯了一个错误并发送了 v2 的解决方案,但我建议任何人使用 v3。如果你不能,请按照v2版本...
v2 没有,但我在这个问题yaml.Node的答案中找到了一个非常相似的解决方案(我在那里修正了一个错字):
type RawMessage struct {
unmarshal func(interface{}) error
}
func (msg *RawMessage) UnmarshalYAML(unmarshal func(interface{}) error) error {
msg.unmarshal = unmarshal
return nil
}
func (msg *RawMessage) Unmarshal(v interface{}) error {
return msg.unmarshal(v)
}
这是一个有趣的技巧,你可以通过将UnmarshalYAML它加载到一个临时结构中来烘焙你自己的函数,然后识别你想要的每种类型,而无需处理 YAML 两次:
func (nc *NodeConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
var ncu struct {
Type string `yaml:"type"`
Config RawMessage `yaml:"config"`
}
var err error
// unmarshall into a NodeConfigUnmarshaler to detect correct type
err = unmarshal(&ncu)
if err != nil {
return err
}
// now, detect the type and covert it accordingly
nc.Type = ncu.Type
switch ncu.Type {
case "request":
cfg := &RequestConfig{}
err = ncu.Config.Unmarshal(cfg)
nc.Config = cfg
case "log":
cfg := &LogConfig{}
err = ncu.Config.Unmarshal(cfg)
nc.Config = cfg
default:
return fmt.Errorf("unknown type %q", ncu.Type)
}
return err
}
v2 和 v3 的示例代码是相同的。
- 1 回答
- 0 关注
- 285 浏览
添加回答
举报