>

在对 Go 语言编码程序进行调试的过程中,经常可能会需要打印 struct 或者 map 这种复合结构的变量的内容,本文总结了并提炼了一些方法来方便的查看这些复杂类型的内容。


##### 1. struct ---

如果已知是某具体的 struct。可以把它先转成 json, 然后使用 json 包提供的 Indent() 或者 MarshalIndent() 方法来打印 pretty json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
type Person struct {
Name string
Age int
}

func main() {
mystruct := Person{"alex", 18}

// 1. MarshalIndent
b1, _ := json.MarshalIndent(mystruct, "", " ")
fmt.Println(string(b1))

// 2. Indent
b, _ := json.Marshal(mystruct)
var prettyJson bytes.Buffer
json.Indent(&prettyJson, b, "", "\t")
fmt.Println(string(prettyJson.Bytes()))
}

##### 2. map --- 如果已知具体 map,直接遍历打印即可。
##### 3. interface ---

1) struct
函数参数是 interface{}, 传入的是 struct。
必须使用 reflect 来遍历输出。
注意: 如果 struct 有嵌套, 则嵌套的部分只会整体打印出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

type Addr struct {
road string
city string
}

type Person struct {
Name string
Age int
}

func printStructInfo(info interface{}) {
fmt.Println(reflect.TypeOf(info))
t := reflect.TypeOf(info)
v := reflect.ValueOf(info)
for i := 0; i < t.NumField(); i++ {
if v.CanInterface() {
s := v.Field(i).Interface()
fmt.Printf("%s %s = %v\n", t.Field(i).Name, t.Field(i).Type, s)
}
}
}
func main() {
pers := Person{"alex", 19, Addr{"jiuxian", "beijing"}}
printInfo(pers)
}

运行结果:

main.Person
Name string = alex
Age int = 19
Addr main.Addr = {jiuxian beijing}

对于有嵌套定义的 struct, 那么如何能够递归打印全部的成员出来呢?
这个就必须用到 Kind() 了, 这个函数会返回底层的数据结构类型。

比如:
如果是 struct, 那么 Kind().String() 就会返回 struct。
而 TypeOf().String() 只会返回 main.Addr 这种类型。

注意: 嵌套的 struct 中成员必须已导出(大写开头),否则无法打印,抛出错误:
cannot return value obtained from unexported field or method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
type Addr struct {
Road string
City string
}

type Person struct {
Name string
Age int
Addr
}

func printStructInfo(info interface{}) {
fmt.Println(reflect.TypeOf(info))
t := reflect.TypeOf(info)
v := reflect.ValueOf(info)
for i := 0; i < t.NumField(); i++ {
if v.CanInterface() {
s := v.Field(i).Interface()
if reflect.TypeOf(s).Kind().String() == "struct" {
printInfo(v.Field(i).Interface())
} else {
fmt.Printf("%s %s = %v\n", t.Field(i).Name, t.Field(i).Type, s)
}
}
}
}
func main() {
pers := Person{"alex", 19, Addr{"jiuxian", "beijing"}}
printInfo(pers)
}

运行结果:

ain.Person
Name string = alex
Age int = 19
main.Addr
Road string = jiuxian
City string = beijing

这样的话, 不管 struct 嵌套多少层, 都可以打印出来了。

2) map

map 跟 struct 有所不同, 遍历 map 时, 需要使用 range 关键字。
而 range 遍历的话需要知道 value 的具体类型,比如: map[string]interface{},value 的类型是 interface{},
如果不知道 interface{} 实际所存的具体类型,就没法遍历打印其内容。

隐刺,进行 range 遍历的时候,需要把 value 进行 type assertion 成已知的 map 类型。

注意:不能把 map[string]int 进行 type assert 成 map[string]interface{}。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

func printMapInfo(leading string, info interface{}) {
t := reflect.TypeOf(info)
// v := reflect.ValueOf(info)

if t.Kind().String() == "map" {
for k, v := range info.(map[string]interface{}) {
tv := reflect.TypeOf(v)
if tv.Kind().String() == "map" {
fmt.Println(k, ":")
printInfo("\t", v)
} else {
fmt.Printf("%s%s : %v\n", leading, k, v)
}
}
}
}

func main() {
my := map[string]interface{}{
"name": "alex",
"age": 18,
"commodity": map[string]interface{}{
"class": "friute",
"price": 10.2}
}
printMapInfo("", my)
}

运行结果:

ommodity :
class : friute
price : 10.2
name : alex
age : 18