亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

強制解組為 interface{} 而不是 map[string]interface{}

強制解組為 interface{} 而不是 map[string]interface{}

Go
牛魔王的故事 2023-02-21 19:03:22
我有以下 YAML 結構:type Pipeline struct {    Name string                  `yaml:"name"`    Nodes map[string]NodeConfig  `yaml:"nodes"`    Connections []NodeConnection `yaml:"connections"`}type NodeConfig struct {    Type   string      `yaml:"type"`    Config interface{} `yaml:"config"`}對于每個NodeConfig,根據 的值Type,我需要檢測 的真實類型Config。switch nc.Type {    case "request":        return NewRequestNode(net, name, nc.Config.(RequestConfig))    case "log":        return NewLogNode(net, name)    //...}這是我從中得到的錯誤:panic: interface conversion: interface {} is map[string]interface {}, not main.RequestConfig我懷疑這是因為當我真的Config希望map[string]interface{}它只是一個interface{}. 我怎樣才能做到這一點?
查看完整描述

1 回答

?
慕哥9229398

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 的示例代碼是相同的。


查看完整回答
反對 回復 2023-02-21
  • 1 回答
  • 0 關注
  • 329 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號