Go / Golang JSON 一些心得

自定义序列化和反序列化

可以实现 json.Marshaler 和 json.Unmarshaler 自定义json的序列化和反序列化

type Tags []string

func (t Tags) MarshalJSON() ([]byte, error) {
    return []byte(strconv.Quote(strings.Join(t, ","))), nil
}

func (t *Tags) UnmarshalJSON(b []byte) error {
    b = bytes.Trim(b, `"`)
    *t = strings.Split(string(b), ",")
    return nil
}

func TestTags(t *testing.T) {
    tags := Tags([]string{"美丽", "性感", "迷人"})
    b, _ := json.Marshal(tags)
    if `"美丽,性感,迷人"` != string(b) {
        t.Error("自定义序列化出错")
    }
    json.Unmarshal([]byte(`"性感,美丽,迷人"`), &tags)
    if tags[0] != "性感" || tags[1] != "美丽" || tags[2] != "迷人" {
        t.Error("自定义反序列化出错")
    }
}  

struct 的 tag

go结构体的字段经常用tag来扩展功能,比如json,我们也可以利用reflect包自定义tag解析(gorm利用结构体tag声明字段和数据库的映射)

type Product struct {
    Name     string  // 不写json tag,默认映射为Name
    RealName string  `json:"-"`               // 表示不序列化,也不反序列化
    Price    float64 `json:"price,string"`    // string 可以把 "16.56" 映射为 float64,会把 13.56 输出为 "13.56"
    Count    int     `json:"count,omitempty"` // omitempty 表示零值不输出
}

func TestProduct(t *testing.T) {
    p1 := Product{
        Name:     "iPhone",
        RealName: "iPhone 8 Pro",
        Price:    13.15,
        Count:    0,
    }
    b, _ := json.Marshal(&p1)
    if `{"Name":"iPhone","price":"13.15"}` != string(b) {
        t.Error("Product 序列化失败")
    }
}

不序列化 time.Time

使用time.Time nil指针表示不序列化json,防止输出非法格式的时间

func TestIgnoreNilTime(t *testing.T) {
    // 使用nil表示不输出时间
    user1 := struct {
        UserId    int        `json:"userId"`
        CreatedAt *time.Time `json:"createdAt,omitempty"`
    }{
        UserId: 100019,
    }
    b, _ := json.Marshal(&user1)
    if `{"userId":100019}` != string(b) {
        t.Error("")
    }
}

忽略字段和格式化字段

type User struct {
    UserId   int     `json:"userId"`
    Phone    string  `json:"phone"`
    Password string  `json:"password"`
    Money    float64 `json:"money"`
}

func TestStructEmbedded(t *testing.T) {
    user1 := User{
        UserId:   100019,
        Phone:    "13112120001",
        Password: "abc123",
        Money:    123.56123,
    }
    user2 := struct {
        User // 嵌入 User 结构
        Phone    string `json:"phone"` // 覆盖 User 结构中的 Phone 实现隐藏手机号
        Password string `json:"password,omitempty"` // 不输出密码
        Money    string `json:"money"` // 会覆盖 User 结构中的 Money
    }{
        User:  user1,
        Phone: filterPhone(user1.Phone),
        Money: fmt.Sprintf("%.2f", user1.Money),
    }
    b, _ := json.Marshal(&user2)
    if `{"userId":100019,"phone":"131****0001","money":"123.56"}` != string(b) {
        t.Error("")
    }
}

func filterPhone(phone string) string {
    if len(phone) != 11 {
        return ""
    }
    return phone[:3] + "****" + phone[7:]
}

结构体嵌入用来共用一些通用字段

结构体复制

简单结构使用手动复制就可以,复杂结构可用开源库来复制
比如 https://github.com/jinzhu/copier

结构体复制时,防止浅拷贝问题

结构体直接复制时,里面的指针或者slice等会指向同一份内存数据,修改时需要注意。
可以采用 [https://github.com/jinzhu/copier]https://github.com/jinzhu/copier 进行深拷贝

结构体嵌入和 json 自定义一起使用,可以在序列化和反序列化时做转换

type UserProfile struct {
    Nickname string `json:"nickname"`
    IdCardNo string `json:"idCardNo"`
}

func (u UserProfile) MarshalJSON() ([]byte, error) {
    type UserProfileAlias UserProfile
    u1 := struct {
        UserProfileAlias
        IdCardNo string `json:"idCardNo"`
    }{
        UserProfileAlias: UserProfileAlias(u),
        IdCardNo:         "****",
    }

    b, _ := json.Marshal(&u1)
    return b, nil
}

type Liver struct {
    UserProfile *UserProfile `json:"userProfile"`
}

func TestLiverJson(t *testing.T) {
    liver := Liver{
        UserProfile: &UserProfile{
            Nickname: "娜娜丫头",
            IdCardNo: "123456",
        },
    }
    b, _ := json.Marshal(&liver)
    if `{"userProfile":{"nickname":"娜娜丫头","idCardNo":"****"}}` != string(b) {
        t.Error("结构体嵌入和json自定义出错")
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值