簡體   English   中英

結構中的golang解組(反序列化)變量類型字典

[英]golang unmarshal (deserialize) variable type dictionary in struct

這是我的問題的一個人為示例,因此請忽略這是通過使用帶有 json 可選參數的單一結構來解決的。

鑒於:

    {
        "name": "alice",
        "address_dict": {
                            "home": { "address": "123 st" },
                            "work": { "address": "456 rd", "suite": "123"}
                        }
    }
type AddressIf interface{}

type AddressHome {
    Address string `json:"address"`
}

type AddressWork {
    Address string `json:"address"`
    Suite string `json:"suite"`
}

type Contact struct {
    Name string `json:"name"`
    AddressMap map[string]AddressIf `json:"address_map"`
}
func(self *Contact) UnmarshalJSON(b []byte) error {
    var objMap map[string]*json.RawMessage
    err := json.Unmarshal(b, &objMap
    if err != nil {
        return err
    }

    var rawAddressMap map[string]*json.RawMessage
    err = json.Unmarshal(*objMap["address_map"], &rawAddressMap)
    if err != nil {
        return err
    }

    // how do we unmarshal everything else in the struct, and only override the handling of address map???
    // <failing code block is here - beg - just a tad, just a tad, just a tad - recursive>
    err = json.Unmarshal(b, self)
    if err != nil {
        return err
    }
    // <failing code block is here - end>

    if nil == self.AddressMap {
        self.AddressMap = make(map[string]AddressIf)
    }

    for key, value := range rawAddressMap {
        switch key {
            case "home":
                dst := &AddressHome{}
                err := json.Unmarshal(*value, dst)
                if err != nil {
                    return err
                } else {
                    self.AddressMap[key] = dst
                }
            case "work":
                dst := &AddressWork{}
                err := json.Unmarshal(*value, dst)
                if err != nil {
                    return err
                } else {
                    self.AddressMap[key] = dst
                }
            default:
                continue
        }
    }
}

我在 json 的這個示例位中只有name參數,但假設我的代碼中有更多。 有沒有辦法對所有參數使用正常/默認解組,然后只手動接管address_dict ,或者一旦我承諾為這個 object 實現接口,我就被困在手動反序列化每個范圍?

我嘗試了以下方法,並很快意識到為什么它不起作用。

    err = json.Unmarshal(b, self)
    if err != nil {
        return err
    }

為避免復制聯系人字段,請使用 object 嵌入來隱藏需要特殊處理的字段。

使用工廠模式來消除跨地址類型的代碼重復。

有關更多信息,請參閱評論:

var addressFactories = map[string]func() AddressIf{
    "home": func() AddressIf { return &AddressHome{} },
    "work": func() AddressIf { return &AddressWork{} },
}

func (self *Contact) UnmarshalJSON(b []byte) error {

    // Declare type with same base type as Contact. This
    // avoids recursion below because this type does not
    // have Contact's UnmarshalJSON method.
    type contactNoMethods Contact

    // Unmarshal to this value. The Contact.AddressMap
    // field is shadowed so we can handle unmarshal of
    // each value in the map.
    var data = struct {
        *contactNoMethods
        AddressMap map[string]json.RawMessage `json:"address_map"`
    }{
        // Note that all fields except AddressMap are unmarshaled
        // directly to Contact.
        (*contactNoMethods)(self),
        nil,
    }

    if err := json.Unmarshal(b, &data); err != nil {
        return err
    }

    // Unmarshal each addresss...

    self.AddressMap = make(map[string]AddressIf, len(data.AddressMap))
    for k, raw := range data.AddressMap {
        factory := addressFactories[k]
        if factory == nil {
            return fmt.Errorf("unknown key %s", k)
        }
        address := factory()
        if err := json.Unmarshal(raw, address); err != nil {
            return err
        }
        self.AddressMap[k] = address
    }
    return nil
}
 

在操場上運行它

在對該問題的評論中,OP 表示不應使用其他類型。 這個答案使用了兩種額外的類型( contactNoMethodsdata的匿名類型)。

另一個答案的替代方法是聲明一個命名的map[string]AddressIf類型並讓它實現json.Unmarshaler接口,那么您不必執行任何臨時/匿名類型和轉換舞蹈。

type AddressMap map[string]AddressIf

func (m *AddressMap) UnmarshalJSON(b []byte) error {
    if *m == nil {
        *m = make(AddressMap)
    }

    raw := make(map[string]json.RawMessage)
    if err := json.Unmarshal(b, &raw); err != nil {
        return err
    }

    for key, val := range raw {
        switch key {
        case "home":
            dst := new(AddressHome)
            if err := json.Unmarshal(val, dst); err != nil {
                return err
            }
            (*m)[key] = dst
        case "work":
            dst := new(AddressWork)
            if err := json.Unmarshal(val, dst); err != nil {
                return err
            }
            (*m)[key] = dst
        }
    }
    return nil
}

https://play.golang.org/p/o7zV8zu9g5X

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM