Go 当中 JSON 常用技巧

json

  1. 忽略 struct 空字段, 使用 omitempty
type User struct{
    Email string `json:"email,omitempty"`
    Pwd  string `json:"pwd,omitempty"`
}

临时粘合两个struct

type BlogPost struct{
    URL string `json:"url"`
}

type Analytics struct{
    Visitors int `json:"visitors"`
}

json.Marshal(struct{
    *BlogPost
    *Analytics
}{post, analytics})

临时切开两个struct

json.Unmarshal([]byte(`{
    "url":"www@163.com",
    "visitors":10
}`), struct{
   *BlogPost
   *Analytics
}{&post, &analytics})

临时改名 struct 的字段

type Cache struct {
    Key    string `json:"key"`
    MaxAge int    `json:"cacheAge"`
}

json.Marshal(struct {
    *Cache
    OmitMaxAge int `json:"cacheAge,omitempty"` // remove bad key
    MaxAge     int `json:"max_age"` // add new key
}{cache, 0, 10})

使用字符串传递数字

type Object struct {
    Num int `json:"num,string"`
}

这个对应的json是 {"num":"100"}, 如果json是 {"num":100} 则会报错

使用 MarshalJSON 支持 time.Time

go 默认会把 time.Time 用字符串方式序列化. 如果想用其他方式表示 time.Time, 需要自定义类型并
定义 MarshalJSON

type timeImplMarshaler time.Time

func (o timeImplMarshaler) MarshalJSON() ([]byte, err) {
    seconds := time.Time(o).Unix()
    return []byte(strconv.FormatInt(seconds, 10)), nil
}

使用 RegisterTypeEncoder 支持 time.Time

jsoniter 能够对不是自定义的 type 定义 JSON 编解码方式. 比如对于 time.Time 可以使用 epoch int64
序列化.

import "github.com/json-iterator/go/extra"

extra.RegisterTimeAsInt64Codec(time.Microsecond)
ouput, err := jsoniter.Marshal(time.Unix(1,1002))
should.Equal("1000001", string(output))

使用 MarshalText, UnmarshalText 支持非字符串作为key的map

虽然 JSON 标准里只支持 string 作为 key 的 map. 但是 go 通过 MarshalText() 接口, 使得
其他类型也可以作为 map 的 key.

type Key struct {
    Name  string `json:"name"`
    Value string `json:"value"`
}

// 必须是传值调用(编码)
// 函数当中不能调用 json.Marshal() 函数
func (k Key) MarshalText() ([]byte, error) {
    data := fmt.Sprintf("%v_%v", k.Name, k.Value)
    return []byte(data), nil
}

// 必须是传指针调用(解码), 否则解析的值是空. 
// 函数当中不能调用 json.Unmarshal() 函数
func (k *Key) UnmarshalText(text []byte) (error) {
    tokens := strings.Split(string(text), "_")
    if len(tokens) == 2 {
        k.Name = tokens[0]
        k.Value = tokens[1]
        return nil
    }
    return errors.New("invalid text")
}

func main() {
    val := map[Key]string{{"12", "22"}: "2"}
    data, err := json.Marshal(val)
    fmt.Println(string(data), err)

    var vv map[Key]string
    json.Unmarshal(data, &vv)
    fmt.Println(vv)
}

使用 json.RawMessage

如果部分 json 文档没有标准格式, 可以把原始的信息使用 []byte 保存下来.

type RawObject struct {
    Key string
    Raw json.RawMessage
}

func main() {
    var data RawObject
    json.Unmarshal([]byte(`{"key":"111", "raw":[1,2,3]}`), &data)
    fmt.Println(string(data.Raw)) // [1,2,3]
}

使用 json.Number

默认情况下, 如果是 interface 对应数字的情况会是 float64 类型的. 如果输入的数字比较大, 这个会有损精度. 可以使用 UseNumber() 启用 json.Number 来用字符串表示数字.

统一更改字段的命名风格

① 经常 JSON 里的字段名和 Go 里的字段名是不一样的. 可以使用 field tag 来修改.

② 但是一个个字段来设置, 太过于麻烦, 如果使用 jsoniter 可以统一设置命名风格.

按照 ①, ② 的方式去选择 JSON 的字段名. 如果方式 ① 的字段名存在, 则使用方式 ①, 否则再使用方式 ②

type T struct {
    UserName      string
    FirstLanguage string `json:"language"`
}

extra.SetNamingStrategy(func(s string) string {
    return strings.ToLower(s)
})
d, _ := jsoniter.Marshal(T{})
fmt.Println(string(d)) // {"username":"","language":""}

支持私有的字段

Go 标准库只支持 public 的 field. jsoniter 额外支持了 private 的 field. 需要使用 SupportPrivateFields()
来开启.

type T struct {
    UserName      string
    FirstLanguage string `json:"language"`
    private       string
}

extra.SetNamingStrategy(func(s string) string {
    return strings.ToLower(s)
})
extra.SupportPrivateFields()

var val T
jsoniter.Unmarshal([]byte(`{
   "username":"", 
   "language":"en", 
   "private":"private"
}`), &val)

原文链接:https://blog.csdn.net/tiechui1994/article/details/111193219

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,928评论 6 509
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,748评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,282评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,065评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,101评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,855评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,521评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,414评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,931评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,053评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,191评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,873评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,529评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,074评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,188评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,491评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,173评论 2 357