omitempty的使用
将结构体转成json作为参数,请求某个服务。希望结构体某字段为空时,解析成的json没有该字段。 这就是omitempty关键字的作用,即忽略空值
如:
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int64 `json:"id"`
Name string `json:"string"`
Gender string `json:"gender,omitempty"`
}
func main() {
s := User{
15,
"XiaoMing",
"男",
}
sJson, err := json.Marshal(s)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("json of s = ", string(sJson))
}
执行结果为:
json of s = {"id":15,"string":"XiaoMing","gender":"男"}
如果gender字段为空,即
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int64 `json:"id"`
Name string `json:"string"`
Gender string `json:"gender,omitempty"`
}
func main() {
s := User{
15,
"XiaoMing",
"",
}
sJson, err := json.Marshal(s)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("json of s = ", string(sJson))
}
则执行结果为:
json of s = {"id":15,"string":"XiaoMing"}
而如果去掉omitempty标签,则执行结果为
json of s = {"id":15,"string":"XiaoMing","gender":""}
omitempty的陷阱
不过该关键字有几个注意事项,或称之为"小坑"
如再增加一个address字段,为一个结构体
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int64 `json:"id"`
Name string `json:"string"`
Gender string `json:"gender,omitempty"`
Address address `json:"address,omitempty"`
}
type address struct {
Country string `json:"country"`
Province string `json:"province"`
City string `json:"accessKey"`
Street string `json:"street"`
}
func main() {
s := User{
15,
"XiaoMing",
"",
address{},
}
sJson, err := json.Marshal(s)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("json of s = ", string(sJson))
}
执行结果为:
json of s = {"id":15,"string":"XiaoMing","address":{"country":"","province":"","accessKey":"","street":""}}
为什么明明Address字段加了omitempty,当其为空值时,json.Marshal时还是有这个字段?
这是因为,omitempty关键字无法忽略掉嵌套结构体
那该如何解决?
可以把Address字段定义为指针类型,这样 Golang 就能知道一个指针的“空值”是多少了,否则面对一个我们自定义的结构, Golang 是猜不出我们想要的空值的...
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int64 `json:"id"`
Name string `json:"string"`
Gender string `json:"gender,omitempty"`
Address *address `json:"address,omitempty"`
}
type address struct {
Country string `json:"country"`
Province string `json:"province"`
City string `json:"accessKey"`
Street string `json:"street"`
}
func main() {
s := User{
15,
"XiaoMing",
"",
nil,
}
sJson, err := json.Marshal(s)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("json of s = ", string(sJson))
}
输出为
json of s = {"id":15,"string":"XiaoMing"}
但如果是
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int64 `json:"id"`
Name string `json:"string"`
Gender string `json:"gender,omitempty"`
Address *address `json:"address,omitempty"`
}
type address struct {
Country string `json:"country"`
Province string `json:"province"`
City string `json:"accessKey"`
Street string `json:"street"`
}
func main() {
s := User{
15,
"XiaoMing",
"",
&address{},
}
sJson, err := json.Marshal(s)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("json of s = ", string(sJson))
}
则输出还是
json of s = {"id":15,"string":"XiaoMing","address":{"country":"","province":"","accessKey":"","street":""}}
另一个陷阱,是对于用 omitempty 定义的 field ,如果给它赋的值恰好等于默认空值的话,在转为 json 之后也不会输出这个 field
参考
omitempty源码
其源码位于src/encoding/asn1/common.go
// Given a tag string with the format specified in the package comment,
// parseFieldParameters will parse it into a fieldParameters structure,
// ignoring unknown parts of the string.
func parseFieldParameters(str string) (ret fieldParameters) {
var part string
for len(str) > 0 {
// This loop uses IndexByte and explicit slicing
// instead of strings.Split(str, ",") to reduce allocations.
i := strings.IndexByte(str, ',')
if i < 0 {
part, str = str, ""
} else {
part, str = str[:i], str[i+1:]
}
switch {
case part == "optional":
ret.optional = true
case part == "explicit":
ret.explicit = true
if ret.tag == nil {
ret.tag = new(int)
}
case part == "generalized":
ret.timeType = TagGeneralizedTime
case part == "utc":
ret.timeType = TagUTCTime
case part == "ia5":
ret.stringType = TagIA5String
case part == "printable":
ret.stringType = TagPrintableString
case part == "numeric":
ret.stringType = TagNumericString
case part == "utf8":
ret.stringType = TagUTF8String
case strings.HasPrefix(part, "default:"):
i, err := strconv.ParseInt(part[8:], 10, 64)
if err == nil {
ret.defaultValue = new(int64)
*ret.defaultValue = i
}
case strings.HasPrefix(part, "tag:"):
i, err := strconv.Atoi(part[4:])
if err == nil {
ret.tag = new(int)
*ret.tag = i
}
case part == "set":
ret.set = true
case part == "application":
ret.application = true
if ret.tag == nil {
ret.tag = new(int)
}
case part == "private":
ret.private = true
if ret.tag == nil {
ret.tag = new(int)
}
case part == "omitempty":
ret.omitEmpty = true
}
}
return
}
json.Rawmessage,也许是这种场景更好的方案
另外,这种场景其实可以用json.Rawmessage:
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int64 `json:"id"`
Name string `json:"string"`
Gender string `json:"gender,omitempty"`
//Address *address `json:"address,omitempty"`
Address json.RawMessage `json:"address,omitempty"`
}
type address struct {
Country string `json:"country"`
Province string `json:"province"`
City string `json:"accessKey"`
Street string `json:"street"`
}
func main() {
s := User{
15,
"XiaoMing",
"",
nil,
}
sJson, err := json.Marshal(s)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("json of s = ", string(sJson))
}
输出为:
json of s = {"id":15,"string":"XiaoMing"}
interface{}也可以
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int64 `json:"id"`
Name string `json:"string"`
Gender string `json:"gender,omitempty"`
//Address *address `json:"address,omitempty"`
//Address json.RawMessage `json:"address,omitempty"`
Address interface{} `json:"address,omitempty"`
}
type address struct {
Country string `json:"country"`
Province string `json:"province"`
City string `json:"accessKey"`
Street string `json:"street"`
}
func main() {
s := User{
15,
"XiaoMing",
"",
nil,
}
sJson, err := json.Marshal(s)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("json of s = ", string(sJson))
}
输出为:
json of s = {"id":15,"string":"XiaoMing"}