最近做了一些将go中结构体输出到json的事情,各种花样输出,在这里整理一下。
问题1:如何将struct转成json?
这是最简单的一种情况,直接使用encoding/json
包中的函数就可以了:
package main
import (
"encoding/json"
"fmt"
)
type Item struct {
Label string
Value int32
URL string
}
func main() {
item := Item{"hello", 127, "http://test.com"}
bdata, err := json.Marshal(item)
if err == nil {
fmt.Println(string(bdata))
} else {
fmt.Println(err)
}
}
结果:
{"Label":"hello","Value":127,"URL":"http://test.com"}
问题2:我想自定义输出json的key怎么弄?
使用struct的tag即可:
type Item struct {
Label string `json:"name"`
Value int32 `json:"value"`
URL string `json:"url"`
}
结果:
{"name":"hello","value":127,"url":"http://test.com"}
问题3:有的字段不是必须的,如果为空就不输出呢?
比如URL字段可能为空,如果为空的话就不希望输出了。
func main() {
item := Item{"hello", 127, ""}
bdata, err := json.Marshal(item)
if err == nil {
fmt.Println(string(bdata))
} else {
fmt.Println(err)
}
}
结果:
{"label":"hello","value":127,"url":""}
可以使用omitempty
标签来达到这个目的:
type Item struct {
Label string `json:"label"`
Value int32 `json:"value"`
URL string `json:"url,omitempty"`
}
结果:
{"label":"hello","value":127}
问题4:我想忽略某一个具体的字段
方法一:可以直接使用这个标签:
type User struct {
Name string `json:"name"`
Email string `json:"email"`
Password string `json:"-"`
}
方法二:如果struct是别人定义的不允许我们修改这个标签,除了我们自定义一个struct外,还可以使用匿名struct:
func main() {
user := User{"valineliu", "1749118121@qq.com", "password"}
bdata, err := json.Marshal(struct {
*User
Password bool `json:"password,omitempty"`
}{
User: &user,
})
if err == nil {
fmt.Println(string(bdata))
}
}
结果:
{"name":"valineliu","email":"1749118121@qq.com"}
这里使用了嵌套结构体的概念,外面的Password
字段覆盖了里面User
的Password
字段,然后通过问题3里的技巧,达到忽略某个字段的目的。
问题5:临时添加一个或多个字段
这个就比较简单了,自定义一个struct即可:
type User struct {
Name string `json:"name"`
Email string `json:"email"`
Password string `json:"password"`
}
type PublicUser struct {
*User
Password bool `json:"password,omitempty"`
Token string `json:"token"`
}
func main() {
user := User{"valineliu", "1749118121@qq.com", "password"}
publicUser := PublicUser{
User: &user,
Token: "test_token",
}
bdata, err := json.Marshal(publicUser)
if err == nil {
fmt.Println(string(bdata))
}
}
结果:
{"name":"valineliu","email":"1749118121@qq.com","token":"test_token"}
问题6:将多个struct组合成一个json
可能一个服务的数据来自不同的接口,需要组合到一个json里,那么同样自定义一个struct就可以了:
type User struct {
Name string `json:"name"`
Email string `json:"email"`
Password string `json:"password"`
}
type Analytics struct {
Blogs int32 `json:"blogs"`
Read int32 `json:"read"`
}
type PublicUser struct {
*User
Password bool `json:"password,omitempty"`
*Analytics
}
func main() {
user := User{"valineliu", "1749118121@qq.com", "password"}
analytics := Analytics{100, 200}
publicUser := PublicUser{
User: &user,
Analytics: &analytics,
}
bdata, err := json.Marshal(publicUser)
if err == nil {
fmt.Println(string(bdata))
}
}
结果:
{"name":"valineliu","email":"1749118121@qq.com","blogs":100,"read":200}
问题7:我能自定义一个struct如何转换成json吗?
比如,我们有这样的结构:
type Article struct {
Title string `json:"title"`
PubTime time.Time `json:"pubtime"`
}
直接Marshal
后会是这样:
{"title":"my_title","pubtime":"2019-12-05T20:52:50.759801+08:00"}
如果我们想pubtime
输出的时间是时间戳,可以自定义输出形式:
func (a Article) MarshalJSON() ([]byte, error) {
type Alias Article
return json.Marshal(struct {
PubTime int64 `json:"pubtime"`
Article
}{
PubTime: a.PubTime.Unix(),
Article: a,
})
}
然而这样是不行的,执行后会报一个fatal error: stack overflow
的错误。这是因为,在自定义的MarshalJSON
函数里直接调用了Article.MarshalJSON
函数,这样就无限调用了,直到报错。
为了避免出现这个错误,可以定义一个别名(Alias):
func (a Article) MarshalJSON() ([]byte, error) {
type Alias Article
return json.Marshal(struct {
PubTime int64 `json:"pubtime"`
Alias
}{
PubTime: a.PubTime.Unix(),
Alias: (Alias)(a),
})
}
结果:
{"pubtime":1575638467,"title":"my_title"}
完美。
结束
这篇文章的结束不是这个话题的结束。关于go语言中使用json还有很多话题可以讨论,这里仅仅是将go中的struct转换成json输出,此外,还有将json转换成struct,以及自定义Marshal
和Unmarshal
函数等话题。