json 解析时用到的结构体标签
- 1 只有导出的结构体成员对外部程序 (json) 可见
- 2 结构体必须解析的字段(required 标签)
- 2.1 结构体标签
- 2.2 json 解析嵌套域
- 2.3 json 编码时会对指针解引用,使用的是实际值
- 2.4 encoding/json.Unmarshal 实现 required 标签
- 2.5 当 json 和 stream 相关时,使用 Encoder/Decoder
- 2.6 定义成 json.RawMessage 的域可以延迟解析
- 2.7 使用 interface 和 json.RawMessage 解析动态 json
- 2.7.1 实现 MarshalJson/UnmarshalJSON 接口
- 2.7.2 将 json 解析成 interface
- 2.7.3 使用指针增加代码检查
- 3 gin/binding.Bind
- 4 相关链接
1 只有导出的结构体成员对外部程序 (json) 可见
下面的代码中无法解析 gender 域。且编码成 json 时,gender 域不会包含
package main import ( "encoding/json" "fmt" ) // JSONStruct a struct to be used in json decode type myJSONStruct struct { Name string Age float64 gender string } var rawJSON = []byte(`{ "name": "kiki", "age": 18, "gender": "female" }`) func main() { var s myJSONStruct err := json.Unmarshal(rawJSON, &s) if err != nil { panic(err) } // [Name=kiki] [Age=18.000000] [gender=] fmt.Printf("[Name=%s] [Age=%f] [gender=%s]\n", s.Name, s.Age, s.gender) buf, err := json.Marshal(s) if err != nil { panic(err) } // [buf={"Name":"kiki","Age":18}] fmt.Printf("[buf=%s]\n", buf) }
2 结构体必须解析的字段(required 标签)
2.1 结构体标签
- 解析 json 到结构体时,不适用结构体的字段会被抛弃。json.Unmarshal 找到结构体对应值的流程。比如给定 json 的 key 是
name
- 1 查找标签名字为 name 的字段
- 2 查找名字为 name 的字段
- 3 查找名字为 Name 等大小写不敏感的匹配字段
- 4 如果都没有找到,就直接忽略这个 key,不会报错。当从众多数据中只选择部分使用时非常方便。
json 的 encode/decode 不支持 required 标签。支持的标签包括
FieldName
指定实际要查找的值omitempty
值为空时不要包含到 JSON 中。当丢弃空属性不想包含在输出时很方便-
跳过一些域。当查找到值时会被解析,但是不会被输出package main import ( "encoding/json" "fmt" ) // JSONStruct a struct to be used in json decode type myJSONStruct struct { Name string `json:"nickname"` Age float64 `json:"-"` Gender string `json:",omitempty"` } var rawJSON = []byte(`{ "nickname": "kiki", "age": 18, "gender": "" }`) func main() { var s myJSONStruct err := json.Unmarshal(rawJSON, &s) if err != nil { panic(err) } // [NickName=kiki] [Age=0.000000] [gender=] fmt.Printf("[NickName=%s] [Age=%f] [gender=%s]\n", s.Name, s.Age, s.Gender) buf, err := json.Marshal(s) if err != nil { panic(err) } // [buf={"nickname":"kiki"}] fmt.Printf("[buf=%s]\n", buf) }
2.2 json 解析嵌套域
package main
import (
"encoding/json"
"fmt"
)
type myName struct {
FirstName string `json:"fname"`
LastName string `json:"lname"`
}
// JSONStruct a struct to be used in json decode
type myJSONStruct struct {
myName
Age float64 `json:"-"`
Gender string `json:",omitempty"`
}
var rawJSON = []byte(`{
"fname": "kiki",
"lname": "kity",
"age": 18,
"gender": ""
}`)
func main() {
var s myJSONStruct
err := json.Unmarshal(rawJSON, &s)
if err != nil {
panic(err)
}
// [FirstName=kiki] [LastName=kity] [Age=0.000000] [gender=]
fmt.Printf("[FirstName=%s] [LastName=%s] [Age=%f] [gender=%s]\n", s.FirstName, s.LastName, s.Age, s.Gender)
buf, err := json.Marshal(s)
if err != nil {
panic(err)
}
// [buf={"fname":"kiki","lname":"kity"}]
fmt.Printf("[buf=%s]\n", buf)
}
2.3 json 编码时会对指针解引用,使用的是实际值
2.4 encoding/json.Unmarshal 实现 required 标签
2.5 当 json 和 stream 相关时,使用 Encoder/Decoder
2.6 定义成 json.RawMessage 的域可以延迟解析
2.7 使用 interface 和 json.RawMessage 解析动态 json
2.7.1 实现 MarshalJson/UnmarshalJSON 接口
JSON 模块包含两个接口
Marshaler
和Unmarshaler
。两个接口都需要一个方法// Marshaler 接口定义了怎么把某个类型 encode 成 JSON 数据 type Marshaler interface { MarshalJSON() ([]byte, error) } // Unmarshaler 接口定义了怎么把 JSON 数据 decode 成特定的类型数据。如果后续还要使用 JSON 数据,必须把数据拷贝一份 type Unmarshaler interface { UnmarshalJSON([]byte) error }
- 如果将这两个接口增加到自定义类型,就可以被编码成 JSON 或者把 JSON 解析成自定义类型
一个很好的例子就是
time.Time
类型type Month struct { MonthNumber int YearNumber int } func (m Month) MarshalJSON() ([]byte, error){ return []byte(fmt.Sprintf("%d/%d", m.MonthNumber, m.YearNumber)), nil } func (m *Month) UnmarshalJSON(value []byte) error { parts := strings.Split(string(value), "/") m.MonthNumber = strconv.ParseInt(parts[0], 10, 32) m.YearNumber = strconv.ParseInt(parts[1], 10, 32) return nil }
2.7.2 将 json 解析成 interface
interface{}
在 Go 中意味着可以是任何东西,Go 在运行时会分配的合适的内存来存储package main import ( "encoding/json" "fmt" ) type myName struct { FirstName string `json:"fname"` LastName string `json:"lname"` } // JSONStruct a struct to be used in json decode type myJSONStruct struct { myName Age float64 `json:"-"` Gender string `json:",omitempty"` } var rawJSON = []byte(`{ "fname": "kiki", "lname": "kity", "age": 18 }`) func main() { var s map[string]interface{} err := json.Unmarshal(rawJSON, &s) if err != nil { panic(err) } fmt.Printf("[map=%v]", s) if s["gender"] == nil { panic("Gender is nil") } }
2.7.3 使用指针增加代码检查
结构体字段使用指针,解析之后判断是否为 nil
package main import ( "encoding/json" "fmt" ) // JSONStruct a struct to be used in json decode type JSONStruct struct { Name *string Age *float64 } var rawJSON = []byte(`{ "name": "We do not provide a Age" }`) func main() { var s *JSONStruct err := json.Unmarshal(rawJSON, &s) if err != nil { panic(err) } if s.Name == nil { panic("Name is missing or null!") } if s.Age == nil { panic("Age is missing or null!") } fmt.Printf("Name: %s Age: %f\n", *s.Name, *s.Age) }
3 gin/binding.Bind
使用
binding:"required"
指定某个域是必须的。当 binding 时该字段为空会返回错误package main import ( "fmt" "time" "net/http" "github.com/gin-gonic/gin" ) type myJSONStruct struct { Name string Age int `binding:"required"` } func addUser(c *gin.Context) { var response interface{} data := new(myJSONStruct) if err := c.Bind(data); err != nil { // [err=Key: 'myJSONStruct.Age' Error:Field validation for 'Age' failed on the 'required' tag] fmt.Printf("addUser error [Bind error] [err=%s]\n", err) c.JSON(http.StatusBadRequest, response) return } fmt.Printf("addUser success [data=%v]\n", data) c.JSON(http.StatusOK, response) } func main() { router := gin.New() api := router.Group("/api/adduser") api.POST("", addUser) httpServer := &http.Server{ Addr: "0.0.0.0:10300", Handler: router, ReadHeaderTimeout: 5 * time.Second, } httpServer.ListenAndServe() }