go中的JSON编组/解组同一个结构到不同的JSON格式?

时间:2014-10-10 16:11:47

标签: json struct go marshalling unmarshalling

我有一个结构,我想根据上下文不同地制作成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"`
    }

有没有一种简单的方法可以做到这一点:

  1. 制作2个独立的结构。
  2. 编写自定义编组程序。
  3. 暂时删除PhoneNumber和MailingAddress的字符串值(标记上有omitempty),编组然后再添加它们。
  4. 如果只有办法:

    1. 指定2组标签并告诉编组人员使​​用哪些标签。
    2. 在运行时动态更改标记。

2 个答案:

答案 0 :(得分:11)

我知道你明确提到"没有编写自定义封送程序"但是如果有人看到这个并且认为应该避免因为复杂性,定制编组来做你想做的事情真的很简单:

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,因为它是通过编码的定制封送者。)

操场上的可运行示例:http://play.golang.org/p/1N_iBzvuW4

答案 1 :(得分:-1)

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

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