Golang的TOML库(toml和go-toml)

TOML 简介

TOML 的全称是 Tom’s Obvious, Minimal Language,因为它的作者是 GitHub 联合创始人 Tom Preston-Werner。

TOML 是目前最优秀的配置文件格式方案,相较于 yaml、json、ini,优势很明显。目前很多新项目直接使用 TOML 作为配置文件的格式,比如 golang 官方的 go.mod。

TOML 的目标是成为一个极简的配置文件格式。TOML 被设计成可以无歧义地被映射为哈希表,从而被多种语言解析。

TOML 语法及简介 https://github.com/toml-lang/toml

toml

TOML 文件

 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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# example.toml

# This is an example TOML document which shows most of its features.

# Simple key/value with a string.
title = "TOML example \U0001F60A"

desc = """
An example TOML document. \
"""

# Array with integers and floats in the various allowed formats.
integers = [42, 0x42, 0o42, 0b0110]
floats   = [1.42, 1e-02]

# Array with supported datetime formats.
times = [
	2021-11-09T15:16:17+01:00,  # datetime with timezone.
	2021-11-09T15:16:17Z,       # UTC datetime.
	2021-11-09T15:16:17,        # local datetime.
	2021-11-09,                 # local date.
	15:16:17,                   # local time.
]

# Durations.
duration = ["4m49s", "8m03s", "1231h15m55s"]

# Table with inline tables.
distros = [
	{name = "Arch Linux", packages = "pacman"},
	{name = "Void Linux", packages = "xbps"},
	{name = "Debian",     packages = "apt"},
]

# Create new table; note the "servers" table is created implicitly.
[servers.alpha]
	# You can indent as you please, tabs or spaces.
	ip        = '10.0.0.1'
	hostname  = 'server1'
	enabled   = false
[servers.beta]
	ip        = '10.0.0.2'
	hostname  = 'server2'
	enabled   = true

# Start a new table array; note that the "characters" table is created implicitly.
[[characters.star-trek]]
	name = "James Kirk"
	rank = "Captain"
[[characters.star-trek]]
	name = "Spock"
	rank = "Science officer"

[undecoded] # To show the MetaData.Undecoded() feature.
	key = "This table intentionally left undecoded"

Golang Code

 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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"time"

	"github.com/BurntSushi/toml"
)

// 目前暂不支持comment
type example struct {
	Title      string
	Desc       string `toml:"desc"`
	Integers   []int
	Floats     []float64
	Times      []time.Time
	Duration   []time.Duration
	Distros    []distro
	Servers    map[string]server
	Characters map[string][]struct {
		Name string
		Rank string
	}
}

type server struct {
	IP       string
	Hostname string
	Enabled  bool
}

type distro struct {
	Name     string
	Packages string
}

type fmtTime struct {
	time.Time
}

func (t fmtTime) String() string {
	f := "2006-01-02 15:04:05.999999999"
	if t.Time.Hour() == 0 {
		f = "2006-01-02"
	}
	if t.Time.Year() == 0 {
		f = "15:04:05.999999999"
	}
	if t.Time.Location() == time.UTC {
		f += " UTC"
	} else {
		f += " -0700"
	}
	return t.Time.Format(`"` + f + `"`)
}

func main() {
	f := "example.toml"
	if _, err := os.Stat(f); err != nil {
		log.Fatalln(0, err)
	}

	var config example
	meta, err := toml.DecodeFile(f, &config) // 从文件读取
	if err != nil {
		log.Fatalln(2, err)
	}

	fmt.Println(meta.Keys())

	fmt.Println(config.Integers)

	fmt.Println(config.Times)

	config.Desc = "hello world"

	buf := bytes.NewBuffer([]byte{})

	err = toml.NewEncoder(buf).Encode(&config) // 将对象编码为TOML
	if err != nil {
		log.Fatalln(3, err)
	}

	ioutil.WriteFile("new.toml", buf.Bytes(), 0666)
}

go-toml

TOML 文件

 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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# This is an example TOML document which shows most of its features.

# Simple key/value with a string.
title = "TOML example \U0001F60A"

desc = """
An example TOML document. \
"""

# Array with integers and floats in the various allowed formats.
integers = [42, 0x42, 0o42, 0b0110]
floats = [1.42, 1e-02]

# Array with supported datetime formats.
# 不支持带时区的时间格式
times = [
	2021-11-09T15:16:17, # local datetime.
	2021-11-09 15:16:17, # local datetime.
	# 2021-11-09,          # local date.
	# 15:16:17,            # local time.
]

# Durations. go-toml不支持这种格式
# duration = ["4m49s", "8m03s", "1231h15m55s"]

# Table with inline tables.
distros = [
	{ name = "Arch Linux", packages = "pacman" },
	{ name = "Void Linux", packages = "xbps" },
	{ name = "Debian", packages = "apt" },
]

# Create new table; note the "servers" table is created implicitly.
[servers.alpha]
# You can indent as you please, tabs or spaces.
ip = '10.0.0.1'
hostname = 'server1'
enabled = false
[servers.beta]
ip = '10.0.0.2'
hostname = 'server2'
enabled = true

# Start a new table array; note that the "characters" table is created implicitly.
[[characters.star-trek]]
name = "James Kirk"
rank = "Captain"
[[characters.star-trek]]
name = "Spock"
rank = "Science officer"

[undecoded] # To show the MetaData.Undecoded() feature.
key = "This table intentionally left undecoded"

Golang Code

 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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"time"

	"github.com/pelletier/go-toml/v2"
)

type example struct {
	Title      string
	Desc       string `toml:"desc" comment:"这是描述\n1231312"`
	Integers   []int  `comment:"这是整数数组"`
	Floats     []float64
	Times      []toml.LocalDateTime
	Duration   []time.Duration
	Distros    []distro
	Servers    map[string]server
	Characters map[string][]struct {
		Name string
		Rank string
	}
}

type server struct {
	IP       string
	Hostname string
	Enabled  bool
}

type distro struct {
	Name     string `comment:"Name"`
	Packages string `comment:"Packages"`
}

func main() {
	f := "example.toml"
	if _, err := os.Stat(f); err != nil {
		log.Fatalln(0, err)
	}

	tomlBytes, err := ioutil.ReadFile("example.toml")
	if err != nil {
		log.Fatalln(1, err)
	}

	var config example
	err = toml.Unmarshal(tomlBytes, &config) // 操作与encoding/json一致
	if err != nil {
		log.Fatalln(2, err)
	}

	fmt.Println(config.Integers)

	fmt.Println(config.Times)

	fmt.Println(config.Times[0].AsTime(time.UTC).Local())

	config.Desc = "hello world"

	newTomlBytes, err := toml.Marshal(&config) // 操作与encoding/json一致
	if err != nil {
		log.Fatalln(3, err)
	}

	ioutil.WriteFile("new.toml", newTomlBytes, 0666)

}