内容简介:从举个例子:在上面的例子中,
从 Go 1.10
起,标准库 encoding/json
提供了方法
func (*Decoder) DisallowUnknownFields
。调用该方法表示,当目标是一个结构,并且输入流中包含任何 不匹配
该结构的非忽略的导出字段时, Decoder
会返回一个错误。
举个例子:
package main
import (
"encoding/json"
"fmt"
"log"
"strings"
)
func main() {
var jsonStream = `{"Name": "Ed", "Text": "Knock knock."}`
type Message struct {
Name string
}
dec := json.NewDecoder(strings.NewReader(jsonStream))
dec.DisallowUnknownFields()
var m Message
err := dec.Decode(&m)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", m.Name)
}
在上面的例子中, jsonStream
定义了一个结构 Message
不存在的字段 "Text"
。接下来,声明一个 Decoder
,并且调用 DisallowUnknownFields
方法。
运行 会发现,反序列化失败:
2009/11/10 23:00:00 json: unknown field "Text"
如果结构 Message
中有一个忽略的导出 Text
字段,又会发生什么呢?稍微改动下上面的代码:
func main() {
var jsonStream = `{"Name": "Ed", "Text": "Knock knock."}`
type Message struct {
Name string
Text string `json:"-"` // 增加一个字段定义
}
dec := json.NewDecoder(strings.NewReader(jsonStream))
dec.DisallowUnknownFields()
var m Message
err := dec.Decode(&m)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", m.Name)
}
运行 ,得到同样的错误:
2009/11/10 23:00:00 json: unknown field "Text"
同理,如果 Text
字段变成未导出字段,也会出现相同的报错:
func main() {
var jsonStream = `{"Name": "Ed", "Text": "Knock knock."}`
type Message struct {
Name string
text string
}
dec := json.NewDecoder(strings.NewReader(jsonStream))
dec.DisallowUnknownFields()
var m Message
err := dec.Decode(&m)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", m.Name)
}
运行 验证一下吧。
Q2:如何判断某个字段是否存在?
Go 结构的零值导致了我们无法通过判断字段值是否等于某个值来确定 JSON 字符串中是否存在某个字段。
此外,如果将字段类型定义为指针的话,则无法区分该字段的值就是 null
的场景。
当然,我们并非无计可施。 encoding/json
允许我们为自定义结构定义序列化和反序列化方法。只要分别实现 MarshalJSON() ([]byte, error)
和 UnmarshalJSON([]byte) error
方法即可。
来看下如何通过 MarshalJSON
方法来判断某个字段是否存在:
type Int struct {
Valid bool // 表示是否为有效值
Set bool // 表示是否设置
Value int
}
// 自定义反序列化方法
func (i *Int) UnmarshalJSON(data []byte) error {
// 如果调用了该方法,说明设置了该值
i.Set = true
if string(data) == "null" {
// 表明该字段的值为 null
return nil
}
var temp int
if err := json.Unmarshal(data, &temp); err != nil {
return err
}
i.Value = temp
i.Valid = true
return nil
}
// 自定义序列化方法
func (i Int) MarshalJSON() ([]byte, error) {
return []byte(strconv.Itoa(i.Value)), nil
}
在上面的例子中,我们定义了一个结构体 Int
来替代基本结构 int
。并且定义了两个字段来表示是否设置及是否有效。这样,在序列化之后,我们就可以通过这两个字段来检查了。
此外,因为不希望序列化后出现这两个布尔值,因此还需要自定义序列化方法 MarshalJSON
。
下面,简单测试一下:
type A struct{ Val Int }
func do(bytes []byte) (A, error) {
var a struct{ Val Int }
err := json.Unmarshal(bytes, &a)
return a, err
}
func main() {
notSet := []byte(`{}`)
setNull := []byte(`{"val": null}`)
setValid := []byte(`{"val": 123}`)
setWrongType := []byte(`{"val": "123"}`)
a, err := do(notSet)
log.Printf("NotSet|set:%t|valid:%t|err: %v\n", a.Val.Set, a.Val.Valid, err)
a, err = do(setNull)
log.Printf("SetNull|set:%t|valid:%t|err: %v\n", a.Val.Set, a.Val.Valid, err)
a, err = do(setValid)
log.Printf("SetValid|set:%t|valid:%t|err: %v\n", a.Val.Set, a.Val.Valid, err)
a, err = do(setWrongType)
log.Printf("SetWrongType|set:%t|valid:%t|err: %v\n", a.Val.Set, a.Val.Valid, err)
}
可以 运行 一下查看结果。
Q3:如何让 omitempty
选项对自定义结构体生效?
如果对字段使用了 omitempty
选项,那么在序列化过程中,如果该字段具有零值(即 false、0、nil 指针、nil 接口值和任何空数组、空 slice、空 map 或者空字符串),那么会忽略该字段。
但是,当前最新的 Go 版本(Go)下,这个选项对于自定义结构是不生效的。
举个例子:
func main() {
type Text struct {
ID int
Content string
}
type Message struct {
Name string
Content Text `json:"content,omitempty"`
}
var m Message
m.Name = "test"
bytes, err := json.Marshal(m)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", string(bytes))
}
在上面的例子中,我们定义了一个结构体 Message
,它包含一个字段 Content
,类型为自定义的 Text
,并打上 omitempty
选项。然后,声明一个实例并给 Message
的另一个字段赋值。接着序列化该实例。
期待序列化结果为 {"Name":"test"}
。但是 运行
之后却得到以下结果:
{"Name":"test","content":{"ID":0,"Content":""}}
查看 encoding/json
相关代码会发现,该库在字段使用了 omitempty
选项时,对于空值的判断确实仅限于文档中描述的:
// encode.go
func isEmptyValue(v reflect.Value) bool {
switch v.Kind() {
case reflect.Array, reflect.Map, reflect.Slice, reflect.String: // 空数组、空 Map、空 slice、空字符串
return v.Len() == 0
case reflect.Bool: // false
return !v.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: // 0
return v.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: // 0
return v.Uint() == 0
case reflect.Float32, reflect.Float64: // 0
return v.Float() == 0
case reflect.Interface, reflect.Ptr: // nil 指针、nil interface 值
return v.IsNil()
}
return false
}
也就是说,此判断逻辑不适用于自定义结构(非指针的情况下)。并且, 除了在使用自定义结构的时候使用指针
,没有其他任何方法可以让 omitempty
选项对自定义结构体生效!!
Issue 11939 提出并跟踪了这个问题。这个 Issue 从 2015 年 7 月份提出至今仍未有确定的解决时间 (`д´)
以上所述就是小编给大家介绍的《Golang/JSON | DisallowUnknownFields 和自定义结构的序列化方法》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Agile Web Application Development with Yii 1.1 and PHP5
Jeffrey Winesett / Packt Publishing / 2010-08-27
In order to understand the framework in the context of a real-world application, we need to build something that will more closely resemble the types of applications web developers actually have to bu......一起来看看 《Agile Web Application Development with Yii 1.1 and PHP5》 这本书的介绍吧!
JS 压缩/解压工具
在线压缩/解压 JS 代码
URL 编码/解码
URL 编码/解码