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 = Member{1,"小明","xiaoming@163.com",1,18} // 简短变量声明方式:m2 := Member{1,"小明","xiaoming@163.com",1,18}

var m3 = Member{id:2,"name":"小红"}// 简短变量声明方式:m3 := Member{id: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 := Person{name: "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.Member{1,"小明","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.Person{string:"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 := Employee{Human{name: "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 := Student{name:"Luffy", age:18 }
    s2 := Student{name:"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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

酷酷的Herio

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值