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"}