JSON在go中将同一结构编组/解编组为不同的JSON格式?

JSON在go中将同一结构编组/解编组为不同的JSON格式?,json,struct,go,marshalling,unmarshalling,Json,Struct,Go,Marshalling,Unmarshalling,我有一个结构,我想根据上下文的不同将其封送到JSON中 例如,有时我想这样封送: type MyStruct struct { Nickname string `json:"nickname"` EmailAddress string `json:"email_address"` PhoneNumber string `json:"-"` MailingAddress string `json:"-"`

我有一个结构,我想根据上下文的不同将其封送到JSON中

例如,有时我想这样封送:

    type MyStruct struct {
        Nickname       string `json:"nickname"`
        EmailAddress   string `json:"email_address"`
        PhoneNumber    string `json:"-"`
        MailingAddress string `json:"-"`
    }
    type MyStruct struct {
        Nickname       string `json:"nickname"`
        EmailAddress   string `json:"email_address"`
        PhoneNumber    string `json:"phone_number"`
        MailingAddress string `json:"mailing_address"`
    }
有时我想这样做:

    type MyStruct struct {
        Nickname       string `json:"nickname"`
        EmailAddress   string `json:"email_address"`
        PhoneNumber    string `json:"-"`
        MailingAddress string `json:"-"`
    }
    type MyStruct struct {
        Nickname       string `json:"nickname"`
        EmailAddress   string `json:"email_address"`
        PhoneNumber    string `json:"phone_number"`
        MailingAddress string `json:"mailing_address"`
    }
有没有一种简单的方法可以做到这一点,而无需:

  • 制作两个独立的结构
  • 编写自定义封送拆收器
  • 暂时删除PhoneNumber和MailingAddress的字符串值(标记上有一个省略的空),封送,然后将它们添加回
  • 如果有办法:

  • 指定2组标记,并告诉封送员要使用哪些标记
  • 在运行时动态更改标记

  • 您可以使用反射,这并不是最有效的解决方案,但它很方便

    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)
    }
    

    我知道您明确提到“不编写自定义封送拆收器”,但如果有人看到这一点并认为由于复杂性应该避免,则自定义封送拆收器执行您想要执行的操作非常简单:

    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)
    }
    
    如果
    all
    字段应由外部包设置,则可以在结构上定义一个方法,或者将该字段公开(不会影响JSON,因为它是通过自定义封送拆收器编码的)


    操场上的可运行示例:

    这比我想象的要简单得多。最后,我结合使用了这种方法和这里描述的方法:这里有一些链接供参考:封送拆收器类型。博客文章:同样的原则也适用于解封-