This is a contrived example of my issue, so please ignore that this is solved by using a singular struct w/ json optional param.
Given:
{
"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
}
}
}
I have just the name
param in this sample bit of json, but let's assume I have more in my code. Is there a way to use the normal/default unmarshal for all the params, and then only manually take over for the address_dict
, or is it the case that once I commit to implementing the interface for this object, I'm stuck manually deserializing each parameter?
I tried the following, and quickly realized why it didn't work.
err = json.Unmarshal(b, self)
if err != nil {
return err
}
An alternative to the other answer would be to declare a named map[string]AddressIf
type and have it implement the json.Unmarshaler
interface, then you do not have to do any of the temporary / anon type and conversion dance.
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
}