Reputation: 697
I have a struct that I'd like to Marshal into JSON differently depending on the context.
For example, sometimes I want to marshal like this:
type MyStruct struct {
Nickname string `json:"nickname"`
EmailAddress string `json:"email_address"`
PhoneNumber string `json:"-"`
MailingAddress string `json:"-"`
}
And sometimes I want to marshal like this:
type MyStruct struct {
Nickname string `json:"nickname"`
EmailAddress string `json:"email_address"`
PhoneNumber string `json:"phone_number"`
MailingAddress string `json:"mailing_address"`
}
Is there a simple way to do this without:
If only there was a way to:
Upvotes: 11
Views: 4285
Reputation: 24003
I know you explicitly mention "without writing a custom marshaler", but in case someone sees this and thinks it should be avoided because of complexity, a custom marshaler to do what you want to do is really simple:
type MyStruct struct {
Nickname string `json:"nickname"`
EmailAddress string `json:"email_address"`
PhoneNumber string `json:"phone_number"`
MailingAddress string `json:"mailing_address"`
all bool
}
func (ms MyStruct) MarshalJSON() ([]byte, error) {
m := map[string]interface{}{} // ideally use make with the right capacity
m["nickname"] = ms.Nickname
m["email_address"] = ms.EmailAddress
if ms.all {
m["phone_number"] = ms.PhoneNumber
m["mailing_address"] = ms.MailingAddress
}
return json.Marshal(m)
}
If the all
field should be set by an external package, then a method could be defined on the struct, or the field could be made public (wouldn't affect the JSON since it is encoded via the custom marshaler).
Runnable example on the playground: http://play.golang.org/p/1N_iBzvuW4
Upvotes: 17
Reputation: 99351
You can use reflection, not really the most efficient solution but it is convenient.
func MarshalSubset(obj interface{}, fields ...string) ([]byte, error) {
if len(fields) == 0 {
return json.Marshal(obj)
}
out := make(map[string]interface{}, len(fields))
val := reflect.ValueOf(obj)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
if val.Kind() != reflect.Struct {
panic("not a struct")
}
typ := val.Type()
for _, f := range fields {
val := val.FieldByName(f).Interface()
rfld, _ := typ.FieldByName(f)
tag := strings.Split(rfld.Tag.Get("json"), ",")
if len(tag) > 0 {
f = tag[0]
}
out[f] = val
}
return json.Marshal(out)
}
Upvotes: 0