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 關注
- 329 瀏覽
添加回答
舉報