当前位置: 首页 > news >正文

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

相关文章:

  • 第十三届蓝桥杯JavaB组省赛F题——最大子矩阵 (AC)
  • 易观分析互联网+慢病管理生态洞察,助力行业升级
  • PIE-engine 教程 ——基于PIE-engine的水体频率变化长时序遥感监测自动计算平台
  • 第十三届蓝桥杯JavaB组国赛G题——背包与魔法 (AC)
  • Vue Admin Template关闭eslint校验,lintOnSave:false设置无效解决办法
  • MMpose初体验--多人姿态检测关键点检测
  • 【重识云原生】第六章容器6.4.2.2节——Pod使用(上)
  • T1023: Hello,World!的大小(信息学一本通C++)
  • 前端工程师4.0
  • 分别将Map以Key或Vale进行排序
  • Selenium之入门
  • (数位dp) 算法竞赛入门到进阶 书本题集
  • 7、乐趣国学—趣谈“圣贤”
  • 35分钟了解sql注入-盲注(三)
  • python求解四位数 青少年编程电子学会python编程等级考试三级真题解析2021年6月
  • 收藏网友的 源程序下载网
  • 【划重点】MySQL技术内幕:InnoDB存储引擎
  • IP路由与转发
  • java8-模拟hadoop
  • js 实现textarea输入字数提示
  • Netty 框架总结「ChannelHandler 及 EventLoop」
  • node-glob通配符
  • PHP 的 SAPI 是个什么东西
  • Python中eval与exec的使用及区别
  • SAP云平台运行环境Cloud Foundry和Neo的区别
  • session共享问题解决方案
  • springMvc学习笔记(2)
  • Three.js 再探 - 写一个跳一跳极简版游戏
  • Tornado学习笔记(1)
  • 关于Flux,Vuex,Redux的思考
  • 推荐一个React的管理后台框架
  • 微信小程序设置上一页数据
  • 我的面试准备过程--容器(更新中)
  • 如何正确理解,内页权重高于首页?
  • ​云纳万物 · 数皆有言|2021 七牛云战略发布会启幕,邀您赴约
  • #NOIP 2014# day.1 生活大爆炸版 石头剪刀布
  • #QT(一种朴素的计算器实现方法)
  • (04)Hive的相关概念——order by 、sort by、distribute by 、cluster by
  • (day 2)JavaScript学习笔记(基础之变量、常量和注释)
  • (ibm)Java 语言的 XPath API
  • (python)数据结构---字典
  • (八)Flask之app.route装饰器函数的参数
  • (八)五种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (论文阅读22/100)Learning a Deep Compact Image Representation for Visual Tracking
  • (已解决)什么是vue导航守卫
  • .gitignore文件—git忽略文件
  • .MSSQLSERVER 导入导出 命令集--堪称经典,值得借鉴!
  • .NET Core Web APi类库如何内嵌运行?
  • .net core webapi 部署iis_一键部署VS插件:让.NET开发者更幸福
  • .NET6使用MiniExcel根据数据源横向导出头部标题及数据
  • .net的socket示例
  • .net反编译工具
  • .Net下使用 Geb.Video.FFMPEG 操作视频文件
  • .net中的Queue和Stack
  • .vollhavhelp-V-XXXXXXXX勒索病毒的最新威胁:如何恢复您的数据?