结构体 (struct) 将多个不同类型的字段集中组成一种复合类型,按声明时的字段顺序初始化。
type user struct {
name string
age byte
}
user := user {"Tom", 2}
typevarvar
// 在函数外部定义匿名结构体并赋值给
var config struct {
APIKey string
OAuthConfig oauth.Config
}// 定义并初始化并赋值给
datadata := struct {
Title string
Users []*User
}{
title,
users
}
二、匿名结构体使用场景
匿名结构体在四种常见情景下的用法。
1、组织全局变量
属于同一类的全局变量可通过匿名结构体组织在一起。
var config struct {
APIKey string
OAuthConfig oauth.Config
}
config.APIKey = "BADC0C0A"
2、数据模版
可在后端把数据组织成前端需要的格式传给渲染模版
package mainimport ( "html/template"
"net/http"
"strings")type Paste struct {
Expiration string
Content []byte
UUID string}func pasteHandler(w http.ResponseWriter, r *http.Request) {
paste_id := strings.TrimPrefix(r.URL.Path, "/paste")
paste := &Paste{UUID: paste_id}
keep_alive := false
burn_after_reading := false
data := struct {
Paste *Paste
KeepAlive bool
BurnAfterReading bool
} {
paste,
keep_alive,
burn_after_reading,
}
t, _ := template.ParseFiles("templates/paste.html")
t.Execute(w, data)
}
dataPaste
Expiration: {{ .Paste.Expiration }}
UUID: {{ .Paste.UUID}}
{{ if .BurnAfterReading }}
BurnAfterReading: True{{ else }}
BurnAfterReading: False{{ end }}
3、测试案例数据
在写测试代码时,经常用到匿名结构体生成用例的输入输出,为了覆盖各个测试维度,通常结合切片使用,构成了测试样例尽可能地覆盖所有可能发生情况。
var indexRuneTests = []struct {
s string
rune rune out int}{
{"a A x", 'A', 2},
{"some_text=some_value", '=', 9},
{"a", 'a', 3},
{"ab", '', 4},
}
4、嵌入式锁 (Embedded lock)
var hits struct {
sync.Mutex
n int}
hits.Lock()
hits.n++
hits.Unlock()
三、案例
手机拥有屏幕、电池、指纹识别等信息,将这些信息填充为 JSON 格式的数据。如果需要选择性地分离 JSON 中的数据则较为麻烦。Golang中的匿名结构体可以方便地完成这个操作。
首先给出完整的代码,然后再讲解每个部分。
package main
import (
"encoding/json"
"fmt"
)
//定义手机屏幕
type Screen01 struct {
Size float64 //屏幕尺寸
ResX, ResY int //屏幕分辨率 水平 垂直
}
//定义电池容量
type Battery struct {
Capacity string
}
//返回json数据
func getJsonData() []byte {
//tempData 接收匿名结构体(匿名结构体使得数据的结构更加灵活)
tempData := struct {
Screen01
Battery
HashTouchId bool // 是否有指纹识别
}{
Screen01: Screen01{Size: 12, ResX: 36, ResY: 36},
Battery: Battery{"6000毫安"},
HashTouchId: true,
}
jsonData, _ := json.Marshal(tempData) //将数据转换为json
return jsonData
}
func main() {
jsonData := getJsonData() //获取json数据
fmt.Println(jsonData)
fmt.Println("=========解析(分离)出的数据是===========")
//自定义匿名结构体,解析(分离)全部数据
allData := struct {
Screen01
Battery
HashTouchId bool
}{}
json.Unmarshal(jsonData, &allData)
fmt.Println("解析(分离)全部结构为:", allData)
//自定义匿名结构体,通过json数据,解析(分离)对应的结构(可以是部分结构)
screenBattery := struct {
Screen01
Battery
}{}
json.Unmarshal(jsonData, &screenBattery) //注意:此处只能为结构体指针(一般参数为interface{},都采用地址引用(即地址传递))
fmt.Println("解析(分离)部分结构:", screenBattery)
//自定义匿名结构体,解析(分离)部分结构
batteryTouch := struct {
Battery
isTouch bool
}{}
json.Unmarshal(jsonData, &batteryTouch)
fmt.Println("解析(分离)部分结构:", batteryTouch)
//自定义匿名结构体,解析(分离)部分不存在的结构
temp1 := struct {
Battery
Detail struct {
Name string
Price uint16
}
}{}
json.Unmarshal(jsonData, &temp1)
fmt.Println("解析(分离)部分不存在的结构", temp1)
//自定义匿名结构体,解析(分离)完全不存在的结构
temp2 := struct {
User string
Price uint16
}{}
json.Unmarshal(jsonData, &temp2)
fmt.Println("解析(分离)完全不存在的结构:", temp2)
}
注:匿名结构体可以组合不同类型的数据,使得处理数据变得更为灵活。尤其是在一些需要将多个变量、类型数据组合应用的场景,匿名结构体是一个不错的选择。
json.Unmarshal(data []byte, v interface{})
//注意:参数 v 只能为结构体指针(一般参数为interface{},都采用地址引用(即地址传递))
代码分析说明:
定义数据结构
首先,定义手机的各种数据结构体,如屏幕和电池,参考如下代码:
- // 定义手机屏幕
- type Screen struct {
- Size float32 // 屏幕尺寸
- ResX, ResY int // 屏幕水平和垂直分辨率
- }
- // 定义电池
- type Battery struct {
- Capacity int // 容量
- }
上面代码定义了屏幕结构体和电池结构体,它们分别描述屏幕和电池的各种细节参数。
准备JSON数据
准备手机数据结构,填充数据,将数据序列化为 JSON 格式的字节数组,代码如下:
- // 生成JSON数据
- func genJsonData() []byte {
- // 完整数据结构
- raw := &struct {
- Screen
- Battery
- HasTouchID bool // 序列化时添加的字段:是否有指纹识别
- }{
- // 屏幕参数
- Screen: Screen{
- Size: 5.5,
- ResX: 1920,
- ResY: 1080,
- },
- // 电池参数
- Battery: Battery{
- 2910,
- },
- // 是否有指纹识别
- HasTouchID: true,
- }
- // 将数据序列化为JSON
- jsonData, _ := json.Marshal(raw)
- return jsonData
- }
代码说明如下:
- 第 4 行定义了一个匿名结构体。这个结构体内嵌了 Screen 和 Battery 结构体,同时临时加入了 HasTouchID 字段。
- 第 10 行,为刚声明的匿名结构体填充屏幕数据。
- 第 17 行,填充电池数据。
- 第 22 行,填充指纹识别字段。
- 第 26 行,使用 json.Marshal 进行 JSON 序列化,将 raw 变量序列化为 []byte 格式的 JSON 数据。
分离JSON数据
调用 genJsonData 获得 JSON 数据,将需要的字段填充到匿名结构体实例中,通过 json.Unmarshal 反序列化 JSON 数据达成分离 JSON 数据效果。代码如下:
- func main() {
- // 生成一段JSON数据
- jsonData := genJsonData()
- fmt.Println(string(jsonData))
- // 只需要屏幕和指纹识别信息的结构和实例
- screenAndTouch := struct {
- Screen
- HasTouchID bool
- }{}
- // 反序列化到screenAndTouch中
- json.Unmarshal(jsonData, &screenAndTouch)
- // 输出screenAndTouch的详细结构
- fmt.Printf("%+v\n", screenAndTouch)
- // 只需要电池和指纹识别信息的结构和实例
- batteryAndTouch := struct {
- Battery
- HasTouchID bool
- }{}
- // 反序列化到batteryAndTouch
- json.Unmarshal(jsonData, &batteryAndTouch)
- // 输出screenAndTouch的详细结构
- fmt.Printf("%+v\n", batteryAndTouch)
- }
代码说明如下:
- 第 4 行,调用 genJsonData() 函数,获得 []byte 类型的 JSON 数据。
- 第 6 行,将 jsonData 的 []byte 类型的 JSON 数据转换为字符串格式并打印输出。
- 第 9 行,构造匿名结构体,填充 Screen 结构和 HasTouchID 字段,第 12 行中的 {} 表示将结构体实例化。
- 第 15 行,调用 json.Unmarshal,输入完整的 JSON 数据(jsonData),将数据按第 9 行定义的结构体格式序列化到 screenAndTouch 中。
- 第 18 行,打印输出 screenAndTouch 中的详细数据信息。
- 第 21 行,构造匿名结构体,填充 Battery 结构和 HasTouchID 字段。
- 第 27 行,调用 json.Unmarshal,输入完整的 JSON 数据(jsonData),将数据按第 21 行定义的结构体格式序列化到 batteryAndTouch 中。
- 第 30 行,打印输出 batteryAndTouch 的详细数据信息。
-