JSON sorts / decouples the same structure into a different JSON format in go?

I have a structure that I would like to marshal in JSON differently depending on the context.

For example, sometimes I want to marshal as follows:

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 an easy way to do this without:

  • Creation of 2 separate structures.
  • Writing a custom marshaller.
  • Temporary removal of string values ​​for PhoneNumber and MailingAddress (with lowering on the tag), marshaling and subsequent addition back.

If there was only a way:

  • Indicate 2 sets of tags and tell the marshal which ones to use.
  • Dynamically change tags at runtime.
+5
source share
2 answers

I know that you explicitly mention "without writing a custom marshal", but if someone sees this and thinks that it should be avoided due to complexity, the custom marshaler to do what you want to do is very 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 must be specified by an external package, then the method can be defined in the structure, or the field can be made public (will not affect JSON, since it is encoded using a custom marshaler).

Runnable example on the playground: http://play.golang.org/p/1N_iBzvuW4

+10
source

You can use reflection, not the most effective solution, but it’s 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) } 

playground

-1
source

Source: https://habr.com/ru/post/1204435/


All Articles