[英]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 表示不應使用其他類型。 這個答案使用了兩種額外的類型( contactNoMethods
和data
的匿名類型)。
另一個答案的替代方法是聲明一個命名的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
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.