• 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

  • 相关阅读:
    YOLOv8改进实战 | 更换损失函数之MPDIOU(2023最新IOU)篇
    java基础运算符 之 关系运算符
    Acwing 3487. 最小面积子矩阵 && 3505. 最长ZigZag子序列
    如何优雅地校验后端接口数据,不做前端背锅侠
    [sd-tagging-helper] How to start and how to add tags for dummies
    Delphi、C# 和 TypeScript 之父 Anders Hejlsberg(安德斯·海尔斯伯格,海神)赶潮AI
    Centos 7 更改 Mysql 5.7 默认数据库存储路径
    理解字符串常量池(JVM)
    (二十六)admin-boot项目之基于注解的数据字段脱敏
    卷积神经网络到底是什么,卷积神经网络是一种
  • 原文地址:https://blog.csdn.net/weixin_45750972/article/details/127097404