Go的Struct结构体和(Json Form tag)

Posted Harris-H

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go的Struct结构体和(Json Form tag)相关的知识,希望对你有一定的参考价值。

Go的Struct结构体和(Json Form tag)

1.Struct

1.1 定义

使用struct关键字可以定义一个结构体,结构体中的成员,称为结构体的字段或属性。

type Member struct 
    id          int
    name, email string
    gender, age int


type Member struct 
    id     int
    name   string
    email  string
    gender int
    age    int

type 和 struct 都是关键字,type表示类型,struct表示结构体。

说明结构体是一个复合类型。

上面的代码中,我们定义了一个包含5个字段的结构体,可以看到,相同类型nameemailgenderage在同一行中定义,但比较好的编程习惯是每一行只定义一个字段。

1.2 声明

直接定义变量,这个使用方式并没有为字段赋初始值,因此所有字段都会被自动赋予自已类型的零值,比如name的值为空字符串"",age的值为0。

var m1 Member//所有字段均为空值

var m2 = Member1,"小明","xiaoming@163.com",1,18 // 简短变量声明方式:m2 := Member1,"小明","xiaoming@163.com",1,18

var m3 = Memberid:2,"name":"小红"// 简短变量声明方式:m3 := Memberid:2,"name":"小红"


另一个实例

func Older(p1, p2 Person) (person , int)
    if p1.age > p2.age 
        return p1, p1.age - p2.age
    
    return p2, p1.age - p2.age


func main()
    var tom Person
    tom.name, tom.age = "tom", 20
    
    bob := Personname: "bob", age: 22
    
    old, diff := Older(tom, bob)
    
    fmt.Printf("%s %d", old.name, diff)
    

1.3 访问字段

通过变量名,使用逗号(.),可以访问结构体类型中的字段,或为字段赋值,也可以对字段进行取址(&)操作。

fmt.Println(m2.name)//输出:小明
m3.name = "小花"
fmt.Println(m3.name)//输出:小花
 
age := &m3.age
*age = 20
fmt.Println(m3.age)//20

1.4 指针结构体


另一个实例

type Command struct 
    Name    string    // 指令名称
    Var     *int      // 指令绑定的变量
    Comment string    // 指令的注释

 
var version int = 1
 
cmd := &Command
 
cmd.Name = "version"
cmd.Var = &version
cmd.Comment = "show version"

1.5 可见性

上面的例子中,我们定义结构体字段名首字母是小写的,这意味着这些字段在包外不可见,因而无法在其他包中被访问,只允许包内访问。

下面的例子中,我们将Member声明在member包中,而后在main包中创建一个变量,但由于结构体的字段包外不可见,因此无法为字段赋初始值,无法按字段还是按索引赋值,都会引发panic错误

package member
type Member struct 
    id     int
    name   string
    email  string
    gender int
    age    int

 
package main
 
fun main()
    var m = member.Member1,"小明","xiaoming@163.com",1,18//会引发panic错误

1.6 Tag

1.7 特点

1.8 方法


1.9 组合

组合,可以理解为定义一个结构体中,其字段可以是其他的结构体,这样,不同的结构体就可以共用相同的字段。

type Animal struct 
    Name   string  //名称
    Color  string  //颜色
    Height float32 //身高
    Weight float32 //体重
    Age    int     //年龄

//奔跑
func (a Animal)Run() 
    fmt.Println(a.Name + "is running")

//吃东西
func (a Animal)Eat() 
    fmt.Println(a.Name + "is eating")

 
type Cat struct 
    a Animal

 
func main() 
    var c = Cat
	    a: Animal
            Name:   "猫猫",
            Color:  "橙色",
            Weight: 10,
            Height: 30,
            Age:    5,
        ,
    
    fmt.Println(c.a.Name)
    c.a.Run()

可以看到,我们定义Cat结构体时,可以把Animal结构体作为Cat的字段。


另一个实例

//Animal 动物
type Animal struct 
    name string

 
func (a *Animal) move() 
    fmt.Printf("%s会动!\\n", a.name)

 
//Dog 狗
type Dog struct 
    Feet    int8
    *Animal //通过嵌套匿名结构体实现继承

 
func (d *Dog) wang() 
    fmt.Printf("%s会汪汪汪~\\n", d.name)

 
func main() 
    d1 := &Dog
        Feet: 4,
        Animal: &Animal //注意嵌套的是结构体指针
            name: "乐乐",
        ,
    
    d1.wang() //乐乐会汪汪汪~
    d1.move() //乐乐会动!

1.9.1 匿名字段

当我们创建结构体时,字段可以只有类型,而没有字段名。这样的字段称为匿名字段(Anonymous Field)。习惯上匿名字段叫内嵌,具名字段叫组合.

上面的例子,我们看到,把Animal结构体作为Cat的字段时,其变量名为a,所以我们访问Animal的方法时,语法为c.a.Run(),这种通过叶子属性访问某个字段类型所带的方法和字段用法非常繁琐。

Go语言支持直接将类型作为结构体的字段,而不需要取变量名,这种字段叫匿名字段,如:

type Lion struct 
	Animal //匿名字段

 
func main()
    var lion = Lion
        Animal
            Name:  "小狮子",
            Color: "灰色",
        ,
    
    lion.Run()
    fmt.Println(lion.Name)

通过上面例子,可以看到,通过匿名字段组合其他类型,而后访问匿名字段类型所带的方法和字段时,不需要使用叶子属性,非常方便。


结构体允许其成员字段在声明时只有类型没有字段名,这种没有名字的字段就称为匿名字段。

//Person 结构体Person类型
type Person struct 
    string
    int

 
func main() 
    p1 := Person
        "pprof.cn",
        18,
    
    fmt.Printf("%#v\\n", p1)        //main.Personstring:"pprof.cn", int:18
    fmt.Println(p1.string, p1.int) //pprof.cn 18
    

匿名字段默认采用类型名作为字段名,由于结构体要求字段名称必须唯一,因此一个结构体中同种类型的匿名字段只能有一个。


1.9.2 同名问题

从上面的例子可以看出来,struct 不仅可以将struct作为匿名字段,自定义类型、内置类型都可以作为匿名字段,也可以进行相应的函数操作。

这里我们有个问题,Person上有一个name属性,如果Student上也有一个name属性,那么我们怎么办呢?其实在go里面,最外层的属性具有有限的访问权限,当你通过Student.name访问的时候是访问Student上的属性。同理,我们可以通过Student.Person访问Person上的属性,如:

type Human struct 
    name stirng 
    age int
    phone string

type Employee struct 
    Human
    phone string

func main()
    bob := EmployeeHumanname: "Bob", age: 12, phone: "777444, "3322"
    // 访问 Employee的phone属性
    fmt.Println("bob phone is ", bob.phone) // bob phone is 3322
    // 访问 Human的phone属性
    fmt.Println("bob's person phone is ", bob.Human.phone)


1.10 自定义构造函数

1.11 结构体比较

如果结构体的全部成员都是可以比较的,那么结构体也是可以比较的,那样的话两个结构体将可以使用 == 或 != 运算符进行比较,但不支持 > 或 < 。

func main() 
    s1 := Studentname:"Luffy", age:18 
    s2 := Studentname:"Luffy", age:18 

    fmt.Println("s1 == s2", s1 == s2) //s1 == s2 true
    fmt.Println("s1 != s2", s1 != s2) //s1 != s2 false

2.JSON tag序列化

2.1 格式说明

常用于对struct结构体字段进行json序列化

2.tag里面加上omitempy,可以在序列化的时候忽略0值或者空值

package main

import (
    "encoding/json"
    "fmt"
)

// Product _
type Product struct 
    Name      string  `json:"name"`
    ProductID int64   `json:"product_id,omitempty"`
    Number    int     `json:"number"`
    Price     float64 `json:"price"`
    IsOnSale  bool    `json:"is_on_sale,omitempty"`
    Amount     int     `json:"amount"`


func main() 
    p := &Product
    p.Name = "Xiao mi 6"
    p.IsOnSale = false
    p.Number = 10000
    p.Price = 2499.00
    p.ProductID = 0
    data, _ := json.Marshal(p)
    fmt.Println(string(data))

结果

"name":"Xiao mi 6","number":10000,"price":2499,"amount":0

// 值为false,0或者空字符串的ProductID和IsOnSalebool都没有出现在最后的json串里。

3.有些时候,我们在序列化或者反序列化的时候,可能结构体类型和需要的类型不一致,这个时候可以指定tag的type支持

package main

import (
    "encoding/json"
    "fmt"
)

// Product _
type Product struct 
    Name      string  `json:"name"`
    ProductID int64   `json:"product_id,string"`
    Number    int     `json:"number,string"`
    Price     float64 `json:"price,string"`
    IsOnSale  bool    `json:"is_on_sale,string"`


type ProductV2 struct 
    Name      string  `json:"name"`
    ProductID int64   `json:"product_id"`
    Number    int     `json:"number"`
    Price     float64 `json:"price"`
    IsOnSale  bool    `json:"is_on_sale"`


func main() 
    var data = `"name":"Xiao mi 6","product_id":"10","number":"10000","price":"2499","is_on_sale":"true"`
    p := &Product
    err := json.Unmarshal([]byte(data), p)
    fmt.Printf("[have type] err:%v,p:%+v\\n", err, p)

    p2 := &ProductV2
    err = json.Unmarshal([]byte(data), p2)
    fmt.Printf("[no type] err:%v,p:%+v\\n", err, p2)

结果

[have type] err:<nil>,p:&Name:Xiao mi 6 ProductID:10 Number:10000 Price:2499 IsOnSale:true
[no type] err:json: cannot unmarshal string into Go struct field ProductV2.product_id of type int64,p:&Name:Xiao mi 6 ProductID:0 Number:0 Price:0 IsOnSale:false

其中json的type如下:

bool, for JSON booleans
float64, for JSON numbers
string, for JSON strings
[]interface, for JSON arrays
map[string]interface, for JSON objects
nil for JSON null

若要在被嵌套结构体整体为空时使其在序列化结果中被忽略,不仅要在被嵌套结构体字段后加上json:“fileName,omitempty”,还要将其改为结构体指针。如:

package main

import (
    "encoding/json"
    "fmt"
)

type BodyInfo struct 
    Weight float64
    Height float64


type Student struct 
    Name      string `json:"name"`
    Age       int64
    *BodyInfo `json:"bodyinfo,omitempty"`  // 要使用指针


func main() 
    s1 := Student
        Name: "jack",
        Age:  20,
    

    data, _ := json.Marshal(s1)
    fmt.Println(string(data))


//结果
"name":"jack","Age":20

3.Form Tag

Gin中提供了模型绑定,将表单数据和模型进行绑定,方便参数校验和使用。

模型绑定:

// 表单数据
type LoginForm struct 
    UserName  string    `form:"username"`    
    Password  string    `form:"password"`
    Email	  string    `form:"email"`  

// model 或 service 层Model
type Email struct 
    Email       string
    Password    string

 
 
func EmailLogin (c *gin.Context) 
    var email LoginForm
    if err := c.ShouldBind(&email); err != nil 
        ...
    
    // 获取表单数据局
    args := Email 
        Email:     email.Email,
        Password:  email.Password,
    
    // 对参数进行后续使用
    ...

通过 form:“email” 对表单email数据进行绑定。然后通过Bind()、ShouldBind()等方法获取参数值。


3.参考文章

传送门1

传送门2

传送门3

传送门4


JSON序列化部分

传送门5

传送门6

以上是关于Go的Struct结构体和(Json Form tag)的主要内容,如果未能解决你的问题,请参考以下文章

Go struct和map转化为JSON串

Go struct和map转化为JSON串

Go struct和map转化为JSON串

go_结构体和方法

golang之结构体和方法

[go]结构体和json转换