Golang匿名结构体的使用

一、结构体基础

结构体 (struct) 将多个不同类型的字段集中组成一种复合类型,按声明时的字段顺序初始化。

type user struct {
  name string
  age byte
}

user := user {"Tom", 2}

定义匿名结构体时没有 type 关键字,与其他定义类型的变量一样,如果在函数外部需在结构体变量前加上 var 关键字,在函数内部可省略 var 关键字。

// 在函数外部定义匿名结构体并赋值给 
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)
}

匿名函授定义并初始化之后赋值给 data 变量,除了把 Paste 结构体对象的值传给前端之外,还额外添加了必要的字段。
写过前后端的同学应该知道,前端有时需要后端返回一个标志变量作为判断条件是否显示某一块内容。

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{},都采用地址引用(即地址传递))

代码分析说明:

定义数据结构

首先,定义手机的各种数据结构体,如屏幕和电池,参考如下代码:

  1. // 定义手机屏幕
  2. type Screen struct {
  3. Size float32 // 屏幕尺寸
  4. ResX, ResY int // 屏幕水平和垂直分辨率
  5. }
  6.  
  7. // 定义电池
  8. type Battery struct {
  9. Capacity int // 容量
  10. }

上面代码定义了屏幕结构体和电池结构体,它们分别描述屏幕和电池的各种细节参数。

准备JSON数据

准备手机数据结构,填充数据,将数据序列化为 JSON 格式的字节数组,代码如下:

  1. // 生成JSON数据
  2. func genJsonData() []byte {
  3. // 完整数据结构
  4. raw := &struct {
  5. Screen
  6. Battery
  7. HasTouchID bool // 序列化时添加的字段:是否有指纹识别
  8. }{
  9. // 屏幕参数
  10. Screen: Screen{
  11. Size: 5.5,
  12. ResX: 1920,
  13. ResY: 1080,
  14. },
  15.  
  16. // 电池参数
  17. Battery: Battery{
  18. 2910,
  19. },
  20.  
  21. // 是否有指纹识别
  22. HasTouchID: true,
  23. }
  24.  
  25. // 将数据序列化为JSON
  26. jsonData, _ := json.Marshal(raw)
  27.  
  28. return jsonData
  29. }

代码说明如下:

  • 第 4 行定义了一个匿名结构体。这个结构体内嵌了 Screen 和 Battery 结构体,同时临时加入了 HasTouchID 字段。
  • 第 10 行,为刚声明的匿名结构体填充屏幕数据。
  • 第 17 行,填充电池数据。
  • 第 22 行,填充指纹识别字段。
  • 第 26 行,使用 json.Marshal 进行 JSON 序列化,将 raw 变量序列化为 []byte 格式的 JSON 数据。

分离JSON数据

调用 genJsonData 获得 JSON 数据,将需要的字段填充到匿名结构体实例中,通过 json.Unmarshal 反序列化 JSON 数据达成分离 JSON 数据效果。代码如下:

  1. func main() {
  2.  
  3. // 生成一段JSON数据
  4. jsonData := genJsonData()
  5.  
  6. fmt.Println(string(jsonData))
  7.  
  8. // 只需要屏幕和指纹识别信息的结构和实例
  9. screenAndTouch := struct {
  10. Screen
  11. HasTouchID bool
  12. }{}
  13.  
  14. // 反序列化到screenAndTouch中
  15. json.Unmarshal(jsonData, &screenAndTouch)
  16.  
  17. // 输出screenAndTouch的详细结构
  18. fmt.Printf("%+v\n", screenAndTouch)
  19.  
  20. // 只需要电池和指纹识别信息的结构和实例
  21. batteryAndTouch := struct {
  22. Battery
  23. HasTouchID bool
  24. }{}
  25.  
  26. // 反序列化到batteryAndTouch
  27. json.Unmarshal(jsonData, &batteryAndTouch)
  28.  
  29. // 输出screenAndTouch的详细结构
  30. fmt.Printf("%+v\n", batteryAndTouch)
  31. }

代码说明如下:

  • 第 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 的详细数据信息。

猜你喜欢

转载自blog.csdn.net/weixin_42117918/article/details/90448756