2 回答

TA贡献1841条经验 获得超3个赞
另一个答案的替代方法是声明一个命名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

TA贡献1821条经验 获得超5个赞
为避免复制联系人字段,请使用对象嵌入来隐藏需要特殊处理的字段。
使用工厂模式来消除跨地址类型的代码重复。
有关更多信息,请参阅评论:
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)。
- 2 回答
- 0 关注
- 144 浏览
添加回答
举报