ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Go xmas2020 学习笔记 12、Structs, Struct tags & JSON

2022-04-08 09:00:58  阅读:262  来源: 互联网

标签:Matt 12 Struct tags Lamine json Employee fmt string


课程地址 go-class-slides/xmas-2020 at trunk · matt4biz/go-class-slides (github.com)

主讲老师 Matt Holiday

image-20220401081031592

12-Structs, Struct tags & JSON

Struct

结构通常是不同类型的聚合,所以有不同类型的字段,通过字段查找值。

type Employee struct {
	Name   string
	Number int
	Boss   *Employee
	Hired  time.Time
}

func main() {
	var e Employee
	fmt.Printf("%T %+[1]v", e)
}
main.Employee {Name: Number:0 Boss:<nil> Hired:0001-01-01 00:00:00 +0000 UTC}

通过 \(\%+v\) 显示结构体的字段。通过点表示法插入值。另外的声明方法

	var e2 = Employee{
		"Matt",
		1,
		nil,
		time.Now(),
	}

这种需要按顺序填写所有字段。我们可以指定字段名就可以只写部分

	var e2 = Employee{
		Name:   "Matt",
		Number: 1,
		Hired:  time.Now(),
	}
	boss := Employee{"Lamine", 2, nil, time.Now()}
	e2.Boss = &boss
	fmt.Printf("%T %+[1]v\n", e2)
main.Employee {Name:Matt Number:1 Boss:0xc00005e100 Hired:2022-04-08 07:40:49.042803 +0800 CST m=+0.006431301}

由于 \(Boss\) 是指针,在 \(e2\) 的输出中显示的是指针。上方代码也可以写成

	boss := &Employee{"Lamine", 2, nil, time.Now()}
	e2.Boss = boss

使 \(boss\) 指向结构体指针,在某种意义上创建结构体,匿名获取指针。


使用 \(map\) 管理所有 \(Employee\) 对象

	c := map[string]*Employee{}
	// c := make(map[string]*Employee)
	c["Lamine"] = &Employee{"Lamine", 2, nil, time.Now()}
	c["Matt"] = &Employee{
		Name:   "Matt",
		Number: 1,
		Boss:   c["Lamine"],
		Hired:  time.Now(),
	}
	fmt.Printf("%T %+[1]v\n", c["Lamine"])
	fmt.Printf("%T %+[1]v\n", c["Matt"])
*main.Employee &{Name:Lamine Number:2 Boss:<nil> Hired:2022-04-08 07:51:11.8676147 +0800 CST m=+0.004987001}
*main.Employee &{Name:Matt Number:1 Boss:0xc00005e040 Hired:2022-04-08 07:51:11.8676147 +0800 CST m=+0.004987001}

Struct Gotcha

	c := map[string]Employee{}
	c["Lamine"] = Employee{"Lamine", 2, nil, time.Now()}
	c["Matt"] = Employee{
		Name:   "Matt",
		Number: 1,
		Boss:   &c["Lamine"],
		Hired:  time.Now(),
	}
	fmt.Printf("%T %+[1]v\n", c["Lamine"])
	fmt.Printf("%T %+[1]v\n", c["Matt"])

修改 \(map\) 存储对象,从结构体指针变为结构体,而 \(Employee\) 内的 \(Boss\) 字段需要一个指针,在这种情况下,假设我们从映射中获取对象,并得到其指针,那么 \(IDE\) 会报错。

invalid operation: cannot take address of c["Lamine"]

映射有限制,你不能获取映射内实体的地址。原因在于每当操作地图的时候,如果我将某些内容插入地图或从地图中删除某些内容,地图可以在内部重新排列,因为哈希表数据结构是动态的,那样获得的地址是非常不安全的,可能会变成过时的指针。


	c["Lamine"] = Employee{"Lamine", 2, nil, time.Now()}
	c["Lamine"].Number++

cannot assign to struct field c["Lamine"].Number in map

如果有一张结构体的映射,对映射中一个该结构体中的值进行修改是不可能的。必须要将结构体的映射修改为结构体指针的映射。


Anonymous Struct Type

func main() {
	var album = struct {
		title  string
		artist string
		year   int
		copies int
	}{
		"The White Album",
		"The Beatles",
		1968,
		1000000000,
	}

	var pAlbum *struct {
		title  string
		artist string
		year   int
		copies int
	}

	fmt.Println(album, pAlbum)
}

基于匿名结构类型,并用结构文字初始化,但并不是特别方便。比如创建一个空的匿名结构体指针的时候。

	var album1 = struct {
		title string
	}{
		"The White Album",
	}

	var album2 = struct {
		title string
	}{
		"The Black Album",
	}
	album1 = album2
	fmt.Println(album1, album2)

可以执行这种赋值操作,将拷贝 \(album2\) 的副本复制给 \(album1\) ,两个匿名结构体具有相同的结构和行为(有相同的字段和字段类型)

type album1 struct {
	title string
}
type album2 struct {
	title string
}

func main() {
	var a1 = album1{
		"The White Album",
	}

	var a2 = album2{
		"The Black Album",
	}
	a1 = a2
    // 	a1 = album1(a2)
	fmt.Println(a1, a2)
}

而在这种情况下会报错,因为他们不是同一个类型名,但是他们是可以互相转换的。

cannot use a2 (variable of type album2) as album1 value in assignment


image-20220408081816170

判断结构体一致的条件

  • 字段一样,字段类型也一样
  • 字段按顺序排列
  • 相同的字段标签

image-20220408081956587

红圈用于包含一些如何以各种方式进行编码的信息协议。比如为 \(json\) 创建 \(key\),当我们查看 \(json\) 的工作原理时它将使用反射。


image-20220408082135061

但如果它们是一致的,可以进行强制转换。


image-20220408082343307

需要注意的是,从 \(go\ 1.8\) 起,不同字段标签不阻碍类型转换。


image-20220408082556037


Make the zero value useful

image-20220408082758958

\(nil\ [\ ]byte\) 可以使用 \(append\),当 \(buffer\) 被创建时就可以直接被使用,不需要做什么前置工作。


Empty structs

image-20220408083215121

\(struct\{\}\) 在内存中作为单例对象存在,构建空结构体集合比布尔值集合更省空间。


JSON

type Response struct {
	Page  int      `json:"page"`
	Words []string `json:"words,omitempty"`
}

func main() {
	r := Response{
		Page:  1,
		Words: []string{"up", "in", "out"},
	}

	j, _ := json.Marshal(r)
	fmt.Println(string(j))
	fmt.Printf("%#v\n", r)

	var r2 Response

	_ = json.Unmarshal(j, &r2)
	fmt.Printf("%#v\n", r2)

	r3 := Response{
		Page: 100,
	}

	j3, _ := json.Marshal(r3)
	fmt.Println(string(j3))
	fmt.Printf("%#v\n", r3)
}

\(json.Marshal()\) 返回字节切片,输出到控制台需要转换成 \(string\)。\(json.Unmarshal\) 需要提供一个结构体指针用于存放解析的数据。\(omitempty\) 关键词用于判空,如果为空就省去。否则转换为 \(json\) 的时候会给该字段默认加 \(null\) 值。

字段都以大写开头,这样它们可以被导出。如果字段名以小写开头,\(json\) 不会对它进行编码。

struct field words has json tag but is not exported

从编译器来看程序是正确的,而从 \(linting\ tool\) 静态分析工具来看会给出一个警告。


image-20220408085123362

标签:Matt,12,Struct,tags,Lamine,json,Employee,fmt,string
来源: https://www.cnblogs.com/linxiaoxu/p/16115243.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有