[转]Golang 中使用 JSON 的小技巧-程序员宅基地

技术标签: golang  json  php  

taowenjson-iterator的作者。 序列化和反序列化需要处理JSON和struct的关系,其中会用到一些技巧。 原文 Golang 中使用 JSON 的小技巧是他的经验之谈,介绍了一些struct解析成json的技巧,以及 json-iterator 库的一些便利的处理。

有的时候上游传过来的字段是string类型的,但是我们却想用变成数字来使用。 本来用一个json:",string" 就可以支持了,如果不知道golang的这些小技巧,就要大费周章了。

参考文章:http://attilaolah.eu/2014/09/10/json-and-struct-composition-in-go/

 

1、临时忽略struct空字段

type User struct {
    Email    string `json:"email"`
    Password string `json:"password"`
    // many more fields…
}

如果想临时忽略掉空Password字段,可以用omitempty:

json.Marshal(struct {
    *User
    Password bool `json:"password,omitempty"`
}{
    User: user,
})

2、临时添加额外的字段

type User struct {
    Email    string `json:"email"`
    Password string `json:"password"`
    // many more fields…
}

临时忽略掉空Password字段,并且添加token字段

json.Marshal(struct {
    *User
    Token    string `json:"token"`
    Password bool `json:"password,omitempty"`
}{
    User: user,
    Token: token,
})

3、临时粘合两个struct

通过嵌入struct的方式:

type BlogPost struct {
    URL   string `json:"url"`
    Title string `json:"title"`
}
type Analytics struct {
    Visitors  int `json:"visitors"`
    PageViews int `json:"page_views"`
}
json.Marshal(struct{
    *BlogPost
    *Analytics
}{post, analytics})

4、一个json切分成两个struct

json.Unmarshal([]byte(`{
  "url": "[email protected]",
  "title": "Attila's Blog",
  "visitors": 6,
  "page_views": 14
}`), &struct {
  *BlogPost
  *Analytics
}{
     &post, &analytics})

5、临时改名struct的字段

type CacheItem struct {
    Key    string `json:"key"`
    MaxAge int    `json:"cacheAge"`
    Value  Value  `json:"cacheValue"`
}
json.Marshal(struct{
    *CacheItem
    // Omit bad keys
    OmitMaxAge omit `json:"cacheAge,omitempty"`
    OmitValue  omit `json:"cacheValue,omitempty"`
    // Add nice keys
    MaxAge int    `json:"max_age"`
    Value  *Value `json:"value"`
}{
    CacheItem: item,
    // Set the int by value:
    MaxAge: item.MaxAge,
    // Set the nested struct by reference, avoid making a copy:
    Value: &item.Value,
})

6、用字符串传递数字

type TestObject struct {
    Field1 int    `json:",string"`
}

这个对应的json是 {"Field1": "100"}

如果json是 {"Field1": 100} 则会报错

7、容忍字符串和数字互转

如果你使用的是jsoniter,可以启动模糊模式来支持 PHP 传递过来的 JSON。 

import "github.com/json-iterator/go/extra"
extra.RegisterFuzzyDecoders()

这样就可以处理字符串和数字类型不对的问题了。比如

var val string
jsoniter.UnmarshalFromString(`100`, &val)

又比如

var val float32
jsoniter.UnmarshalFromString(`"1.23"`, &val)

8、容忍空数组作为对象

PHP另外一个令人崩溃的地方是,如果 PHP array是空的时候,序列化出来是[]。但是不为空的时候,序列化出来的是{"key":"value"}。 我们需要把 [] 当成 {} 处理。

如果你使用的是jsoniter,可以启动模糊模式来支持 PHP 传递过来的 JSON。

import "github.com/json-iterator/go/extra"
extra.RegisterFuzzyDecoders()

这样就可以支持了

var val map[string]interface{}
jsoniter.UnmarshalFromString(`[]`, &val)

9、使用 MarshalJSON支持time.Time

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

type timeImplementedMarshaler time.Time
func (obj timeImplementedMarshaler) MarshalJSON() ([]byte, error) {
    seconds := time.Time(obj).Unix()
    return []byte(strconv.FormatInt(seconds, 10)), nil
}

序列化的时候会调用 MarshalJSON

type TestObject struct {
    Field timeImplementedMarshaler
}
should := require.New(t)
val := timeImplementedMarshaler(time.Unix(123, 0))
obj := TestObject{val}
bytes, err := jsoniter.Marshal(obj)
should.Nil(err)
should.Equal(`{
     "Field":123}`, string(bytes))

10、使用 RegisterTypeEncoder支持time.Time

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

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

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

如果要自定义的话,参见 RegisterTimeAsInt64Codec 的实现代码

11、使用 MarshalText支持非字符串作为key的map

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

f, _, _ := big.ParseFloat("1", 10, 64, big.ToZero)
val := map[*big.Float]string{f: "2"}
str, err := MarshalToString(val)
should.Equal(`{
     "1":"2"}`, str)

其中 big.Float 就实现了 MarshalText()

12、使用 json.RawMessage

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

type TestObject struct {
    Field1 string
    Field2 json.RawMessage
}
var data TestObject
json.Unmarshal([]byte(`{"field1": "hello", "field2": [1,2,3]}`), &data)
should.Equal(` [1,2,3]`, string(data.Field2))

13、使用 json.Number

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

decoder1 := json.NewDecoder(bytes.NewBufferString(`123`))
decoder1.UseNumber()
var obj1 interface{}
decoder1.Decode(&obj1)
should.Equal(json.Number("123"), obj1)

jsoniter 支持标准库的这个用法。同时,扩展了行为使得 Unmarshal 也可以支持 UseNumber 了。

json := Config{UseNumber:true}.Froze()
var obj interface{}
json.UnmarshalFromString("123", &obj)
should.Equal(json.Number("123"), obj)

14、统一更改字段的命名风格

经常 JSON 里的字段名 Go 里的字段名是不一样的。我们可以用 field tag 来修改。

output, err := jsoniter.Marshal(struct {
    UserName      string `json:"user_name"`
    FirstLanguage string `json:"first_language"`
}{
    UserName:      "taowen",
    FirstLanguage: "Chinese",
})
should.Equal(`{
     "user_name":"taowen","first_language":"Chinese"}`, string(output))

但是一个个字段来设置,太麻烦了。如果使用 jsoniter,我们可以统一设置命名风格。

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

extra.SetNamingStrategy(LowerCaseWithUnderscores)
output, err := jsoniter.Marshal(struct {
    UserName      string
    FirstLanguage string
}{
    UserName:      "taowen",
    FirstLanguage: "Chinese",
})
should.Nil(err)
should.Equal(`{
     "user_name":"taowen","first_language":"Chinese"}`, string(output))

15、使用私有的字段

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

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

extra.SupportPrivateFields()
type TestObject struct {
    field1 string
}
obj := TestObject{}
jsoniter.UnmarshalFromString(`{
     "field1":"Hello"}`, &obj)
should.Equal("Hello", obj.field1)

下面是我补充的内容

16、忽略掉一些字段

原文中第一节有个错误,我更正过来了。omitempty不会忽略某个字段,而是忽略空的字段,当字段的值为空值的时候,它不会出现在JSON数据中。

如果想忽略某个字段,需要使用 json:"-"格式。

type User struct {
    Email    string `json:"email"`
    Password string `json:"password"`
    // many more fields…
}

如果想临时忽略掉空Password字段,可以用-:

json.Marshal(struct {
    *User
    Password bool `json:"-"`
}{
    User: user,
})

17、忽略掉一些字段2

如果不想更改原struct,还可以使用下面的方法:

type User struct {
    Email    string `json:"email"`
    Password string `json:"password"`
    // many more fields…
}
type omit *struct{}
type PublicUser struct {
    *User
    Password omit `json:"-"`
}
json.Marshal(PublicUser{
    User: user,
})

 

转自:http://colobu.com/2017/06/21/json-tricks-in-Go/ 

转载于:https://www.cnblogs.com/duhuo/p/4186395.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_30734435/article/details/97787853

智能推荐

Qt加载cc.p12证书_qt 使用p12证书-程序员宅基地

文章浏览阅读1.2k次。以前做Qt的https通信时,在代码里加入适配置https的配置就可以了,现在的项目是要与Java服务器进行通信,而且使用java导出的p12的证书,研究了很久,终于在github上找到了方法._qt 使用p12证书

树莓派开机自动运行python程序的方法_树莓派开机自启动python程序-程序员宅基地

文章浏览阅读1.3w次,点赞20次,收藏241次。方法一 添加程序到rc.local 里添加到rc.local 里的py脚本文件将会在树莓派开机是自动运行,这种方式适合没有显示器,具体操作如下:编辑文件 rc.local`sudo nano /etc/rc.local添加命令来执行python程序,要使用文件的绝对路径。确保在exit 0 上面,注意!如果程序里有死循环,一定要在最后加上 & 表示在后台运行 ,然后保存文件并退出。(Ctrl o 回车保存 Ctrl x退出),如下图将会在开机时运行samlpe.py程序, 现在基本都_树莓派开机自启动python程序

与众不同的Java_special jiva actualiser of awareness-程序员宅基地

文章浏览阅读75次。Java与世界的初次见面——Java产生史故事版 2021年的一天,这为29岁的工程师回到家中,放下手中的各种工具,静静地躺在贵妃榻上,望着窗外远方的山,端起手边的咖啡微抿一口,思绪又渐渐回到了30年前。那时的他刚刚出生,但根据这个世界的规定,未满三岁的新生儿不能够登记并拥有自己的名字,于是父母便临时给它起了一个名字叫Oak。Oak从小就要与众不同,他常常用自己与众不同的方法解决前辈们所处的各种问题,但就算如此,他依然没有得到前辈们的重视,只是像同辈们一样,渐渐长到了3岁,而就在他三岁的这..._special jiva actualiser of awareness

kepserver在设备上添加项目失败_隔空投送存储项目失败怎么办-程序员宅基地

文章浏览阅读1.5k次。iphone隔空投送失败是什么原因?IOS系统下有两个并列又超级强大的功能就是Airplay屏幕镜像与AirDrop隔空投送,Airplay屏幕镜像,其实经常玩手机投屏的水果粉应该都知道,Airplay屏幕镜像可以进行多屏幕之间的一个互动功能,播放模式为镜像模式,也就是手机与投屏的大屏幕为同步显示状态。AirDrop隔空投送:(推荐学习:web前端视频教程)AirDrop是Apple在设备之间以无..._kepserver尝试添加项''失败

fine-tune 微调 Transfer learning 迁移学习 动手学深度学习v2_finetune——transfer-程序员宅基地

文章浏览阅读398次。1. Fine-tuning 微调 Transfer learningFine-tuning 微调是整个深度学习领域是最重要的技能。如果前面的知识只能选择一个,那就是微调了。Transfer learning迁移学习,就是可以用别的成功网络的参数等来训练跟你相似的模型。简单来说是重用。跟详细知识请参考: Transfer learning 迁移学习指南2. 代码实现3. Q&Afine tuning和transfer training实际上是同_finetune——transfer

python使用docx对齐表格_Python docx库文本对齐-程序员宅基地

文章浏览阅读1.7k次。我正在使用python docx库来操作word文档.但是我找不到该库的文档页面中的一条线与中心对齐.我也找不到Google.from docx import Documentdocument = Document()p = document.add_paragraph('A plain paragraph having some ')p.add_run('bold').bold = Truep...._python-doc table 右对齐

随便推点

Apache 2.2无法启动:以一种访问权限不允许的方式做了一个访问套接字的尝试_安装阿帕奇报错:以一种访问权限-程序员宅基地

文章浏览阅读395次。2021年国庆节,在某个项目上忙了一天,晚上准备在远程服务器上发布,安装Apache之后无法启动,提示:以一种访问权限不允许的方式做了一个访问套接字的尝试网上搜索,再结合客户说服务器上有IIS的情况,判断是80端口被IIS占用了。将httpd配置文件中的监听端口改成其他的即可,比如88:Listen 88问题解决,Apache启动成功。..._安装阿帕奇报错:以一种访问权限

vue中使用swiper,左右箭头点击没有效果的解决方法_vue cursor: pointer 不生效-程序员宅基地

文章浏览阅读5.2k次。swiper作为一个开源的前端组件,主要用来做各种页面切换轮播的效果。在做左右切换效果时,发现点击左右箭头没有效果,原来是需要在左右箭头的页面标签上添加点击事件才行,代码如下,亲测可用<swiper ref="mySwiper" :options="swiperOptions"> <swiper-slide><div style="background-color: #5cb85c;height: 100%"><img src=_vue cursor: pointer 不生效

Unity自动打包工具_unity混淆工具obfuscar-程序员宅基地

文章浏览阅读2.4w次,点赞10次,收藏25次。Unity一键打包工具,一键生成几十个平台/渠道的安装包。_unity混淆工具obfuscar

mdns-repeater Multicast DNS repeater-程序员宅基地

文章浏览阅读1.1k次。https://github.com/lucasec/mdns-repeater代码来自上面的链接,这个东西有什么用处了1:模仿程序,可以显示多播数据的转发2:对 mDNS的数据进行转发3: 多播数据是无法穿越路由NAT,如 openwrt中的WAN和LAN对于的网络接口4:mdns-repeater 使用直接 接网络接口名称mdns-repeatereth0 br-lan 就可以实现数据在接口之间的转发5:mDNS 的简介mDNS , multicast DN.....

Python中.npz文件的读取_查看npz结构-程序员宅基地

文章浏览阅读8.5w次,点赞68次,收藏147次。有时候从网上下载的数据集扩展名(后缀名)是npz,我们需要对数据进行加载(读取):例如:识别猫狗图片的二分类,下的数据集分别为cat.npz和dog.npzimport numpy as npcat_data = np.load(‘cat.npz’)dog_data = np.load(‘dog.npz’)因为以npz结尾的数据集是压缩文件,里面还有其他的文件使用:cat_data.f..._查看npz结构

《异常检测——从经典算法到深度学习》10 Bagel: 基于条件 VAE 的鲁棒无监督KPI异常检测_robust and unsupervised kpi anomaly detection base-程序员宅基地

文章浏览阅读7.2k次,点赞26次,收藏18次。《异常检测——从经典算法到深度学习》0 概论1 基于隔离森林的异常检测算法 2 基于LOF的异常检测算法3 基于One-Class SVM的异常检测算法4 基于高斯概率密度异常检测算法5 Opprentice——异常检测经典算法最终篇6 基于重构概率的 VAE 异常检测7 基于条件VAE异常检测8 Donut: 基于 VAE 的 Web 应用周期性 KPI 无监督异常检测9 基于条件 VAE 的鲁棒无监督KPI异常检测2018 Robust and Unsupervised K_robust and unsupervised kpi anomaly detection based on conditional variation

推荐文章

热门文章

相关标签