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
参数,但假设我的代码中有更多参数。有没有办法对所有参数使用 normal/default 解组,然后只手动接管 address_dict
,或者一旦我承诺为这个对象实现接口,我在手动反序列化每个参数时卡住了?
我尝试了以下方法,并很快意识到为什么它不起作用。
err = json.Unmarshal(b, self)
if err != nil {
return err
}
为避免复制联系人字段,使用对象嵌入来隐藏需要特殊处理的字段。
使用工厂模式消除地址类型之间的代码重复。
查看评论了解更多信息:
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
}
这是我的问题的人为示例,所以请忽略这是通过使用带有 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
参数,但假设我的代码中有更多参数。有没有办法对所有参数使用 normal/default 解组,然后只手动接管 address_dict
,或者一旦我承诺为这个对象实现接口,我在手动反序列化每个参数时卡住了?
我尝试了以下方法,并很快意识到为什么它不起作用。
err = json.Unmarshal(b, self)
if err != nil {
return err
}
为避免复制联系人字段,使用对象嵌入来隐藏需要特殊处理的字段。
使用工厂模式消除地址类型之间的代码重复。
查看评论了解更多信息:
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
}