TOML简介及NSQD对TOML的使用

1、什么是TOML?

TOML的全称是 [Tom's Obvious Minimal Language],其作者是 GitHub联合创始人 。

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

官网地址:https://toml.io/en/

2、官方示例

 

# This is a TOML document.

title = "TOML Example"

[owner]
name = "Tom Preston-Werner"
dob = 1979-05-27T07:32:00-08:00 # First class dates #日期时间是一等公民

[database]
server = "192.168.1.1"
ports = [ 8000, 8001, 8002 ]
connection_max = 5000
enabled = true

[servers]

  # 随意缩进
  [servers.alpha]
  ip = "10.0.0.1"
  dc = "eqdc10"

  [servers.beta]
  ip = "10.0.0.2"
  dc = "eqdc10"

[clients]
data = [ ["gamma", "delta"], [1, 2] ]

# 可以在数组里面换行
hosts = [
  "alpha",
  "omega"
]

1)注释

TOML认为所有的配置文件都应该支持注释

 

# This is a TOML comment

# This is a multiline
# TOML comment

2)数值

整数、浮点数、无穷大甚至NaN都支持。您可以使用科学记数法,甚至数千个分隔符

# integers
int1 = +99
int2 = 42
int3 = 0
int4 = -17

# hexadecimal with prefix `0x`
hex1 = 0xDEADBEEF
hex2 = 0xdeadbeef
hex3 = 0xdead_beef

# octal with prefix `0o`
oct1 = 0o01234567
oct2 = 0o755

# binary with prefix `0b`
bin1 = 0b11010110

# fractional 小数
float1 = +1.0
float2 = 3.1415
float3 = -0.01

# exponent 指数
float4 = 5e+22
float5 = 1e06
float6 = -2E-2

# both
float7 = 6.626e-34

# separators 分隔符
float8 = 224_617.445_991_228

# infinity
infinite1 = inf # positive infinity
infinite2 = +inf # positive infinity
infinite3 = -inf # negative infinity

# not a number
not1 = nan
not2 = +nan
not3 = -nan 

 

3) Boolean

布尔值总是小写

bool1 = true
bool2 = false

 

4)强大的字符串

有四种方式来表达字符串:基本表达方式、多行基本表达方式、字面量、多行字面量

基本表达方式:用单个双引号括起来

 

str1 = "I'm a string."
str2 = "You can \"quote\" me."
str3 = "Name\tJos\u00E9\nLoc\tSF."
str = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF."

任何Unicode字符都可以使用,除了这些必须转义的字符外:引号、反斜杠和tab以外的控制字符(U+0000到U+0008, U+000A到U+001F, U+007F)

为了方便起见,一些常用的字符有一个紧凑的转义序列:

\b         - backspace       (U+0008)
\t         - tab             (U+0009)
\n         - linefeed        (U+000A)
\f         - form feed       (U+000C)
\r         - carriage return (U+000D)
\"         - quote           (U+0022)
\\         - backslash       (U+005C)
\uXXXX     - unicode         (U+XXXX)
\UXXXXXXXX - unicode         (U+XXXXXXXX)

 

任何Unicode字符都可以用\uXXXX或\UXXXXXXXX格式进行转义。转义码必须是有效的Unicode标量值。

多行基本表达方式:用三个双引号括起来,并且允许换行。包括一个行尾反斜杠在自动消除任何非空格字符前面的空格。

 

str1 = """
Roses are red
Violets are blue"""

str2 = """\
  The quick brown \
  fox jumps over \
  the lazy dog.\
  """

str2 变为"The quick brown fox jumps over the lazy dog."(一个没有换行的句子)

字面量:用单个单引号括起来,不执行转义。

path = 'C:\Users\nodejs\templates'
path2 = '\\User\admin$\system32'
quoted = 'Tom "Dubs" Preston-Werner'
regex = '<\i\c*\s*>'

 

由于没有转义,所以无法在由单引号括起来的字符串中写入单引号。这就是多行字符串字面量的由来:

re = '''\d{2} apps is t[wo]o many'''
lines = '''
The first newline is
trimmed in raw strings.
All other whitespace
is preserved.
'''

 

5)日期和时间

TOML支持日期、时间和日期时间。包含和不包含时间间隔都可以。

 

# offset datetime
odt1 = 1979-05-27T07:32:00Z
odt2 = 1979-05-27T00:32:00-07:00
odt3 = 1979-05-27T00:32:00.999999-07:00

# local datetime
ldt1 = 1979-05-27T07:32:00
ldt2 = 1979-05-27T00:32:00.999999

# local date
ld1 = 1979-05-27

# local time
lt1 = 07:32:00
lt2 = 00:32:00.999999

6)键值对 Key/Value

key = "value"

 

key 和 value 之间的空格会忽略。

key,=,和value必须在同一行。

value必须是下面的一种类型:String,Integer,Float,Boolean,Offset Date-Time,Local Date-Time,Local Date,Local Time,Array,Inline Table

未指定Value是无效的:

 

key = # INVALID

键/值对之后必须有一个换行符(或EOF):

first = "Tom" last = "Preston-Werner" # INVALID

 

7)Keys

Key中可以是基本表示、带引号和带"." 符号的

基本Key的表示只能包含ASCII字母、ASCII数字、下划线和横线(A-Za-z0-9_-)。注意,只允许由ASCII数字组成,例如1234,但是总是被解释为字符串。

 

key = "value"
bare_key = "value"
bare-key = "value"
1234 = "value"

带引号的Key:

"127.0.0.1" = "value"
"character encoding" = "value"
"ʎǝʞ" = "value"
'key2' = "value"
'quoted "value"' = "value"

 

遵循与基本字符串或字符串字面量完全相同的规则,并允许您使用更广泛的键名集合。但是,没有必要,不推荐使用。

通常情况下Key必须是非空的,但是空的带引号的键是允许的(尽管不鼓励)。

 

= "no key name"  # INVALID
"" = "blank"     # VALID but discouraged
'' = 'blank'     # VALID but discouraged

带“.”符号的Key:可以将相似的属性分组在一起

name = "Orange"
physical.color = "orange"
physical.shape = "round"
site."google.com" = true

 

转换成JSON格式,会如下展示:

{
  "name": "Orange",
  "physical": {
    "color": "orange",
    "shape": "round"
  },
  "site": {
    "google.com": true
  }
}

“.”周围的空格将会被忽略,最佳实践是不要使用任何无关的空白

fruit.name = "banana"     # this is best practice
fruit. color = "yellow"    # same as fruit.color
fruit . flavor = "banana"   # same as fruit.flavor

 

缩进被认为空格符将会被忽略。

多次定义一个键是无效的

 

# DO NOT DO THIS
name = "Tom"
name = "Pradyun"

注意带引号的Key和不带引号的Key是等价的:

# THIS WILL NOT WORK
spelling = "favorite"
"spelling" = "favourite"

 

只要没有直接定义键,您仍然可以对它和其中的名称进行写操作。

 

# This makes the key "fruit" into a table.
fruit.apple.smooth = true

# So then you can add to the table "fruit" like so:
fruit.orange = 2

# THE FOLLOWING IS INVALID

# This defines the value of fruit.apple to be an integer.
fruit.apple = 1

# But then this treats fruit.apple like it's a table.
# You can't turn an integer into a table.
fruit.apple.smooth = true

不鼓励定义无序的带点符号的Key :

# VALID BUT DISCOURAGED

apple.type = "fruit"
orange.type = "fruit"

apple.skin = "thin"
orange.skin = "thick"

apple.color = "red"
orange.color = "orange"

 

# RECOMMENDED

apple.type = "fruit"
apple.skin = "thin"
apple.color = "red"

orange.type = "fruit"
orange.skin = "thick"
orange.color = "orange"

因为最基本的Key能够用ASCII整数组成,可能带点符号的Key写成像浮点数float,不建议这样使用。

3.14159 = "pi"

 

映射成 JSON格式会是这样:

{ "3": { "14159": "pi" } }

8)Array

数组是方括号,里面有值。空格将被忽略。元素之间用逗号分隔。数组可以包含键/值对中允许的相同数据类型的值。不同类型的值可以混合使用。

integers = [ 1, 2, 3 ]
colors = [ "red", "yellow", "green" ]
nested_arrays_of_ints = [ [ 1, 2 ], [3, 4, 5] ]
nested_mixed_array = [ [ 1, 2 ], ["a", "b", "c"] ]
string_array = [ "all", 'strings', """are the same""", '''type''' ]

# Mixed-type arrays are allowed
numbers = [ 0.1, 0.2, 0.5, 1, 2, 5 ]
contributors = [
  "Foo Bar <foo@example.com>",
  { name = "Baz Qux", email = "bazqux@example.com", url = "https://example.com/bazqux" }
]

 数组可以跨多行。允许在数组的最后一个值之后使用终止逗号(也称为尾随逗号)。值、逗号和右括号之前可以有任意数量的换行符和注释。数组值和逗号之间的缩进被视为空格并被忽略。

integers2 = [
  1, 2, 3
]

integers3 = [
  1,
  2, # this is ok
]

9)Table

表(也称为哈希表或字典)是键/值对的集合。它们由标题定义,在一行中单独使用方括号。你可以区分头文件和数组,因为数组永远只是值。

[table]

 

在它下面,直到下一个头或EOF,是该表的键/值。表中的键/值对不能保证有任何特定的顺序。

 

[table-1]
key1 = "some string"
key2 = 123

[table-2]
key1 = "another string"
key2 = 456

Table的命名规则与Key的命名规则相同:

[dog."tater.man"]
type.name = "pug"

 

转换成的JSON格式如下:

 

{ "dog": { "tater.man": { "type": { "name": "pug" } } } }

Key周围的空格被忽略。然而,最佳实践是不要使用任何无关的空白。

[a.b.c]            # this is best practice
[ d.e.f ]          # same as [d.e.f]
[ g .  h  . i ]    # same as [g.h.i]
[ j . "ʞ" . 'l' ]  # same as [j."ʞ".'l']

 

缩进被视为空白并被忽略。

你不需要指定所有的超级表。TOML可以自动处理。

 

# [x] you
# [x.y] don't
# [x.y.z] need these
[x.y.z.w] # for this to work

[x] # defining a super-table afterward is ok

 

空表是允许的,并且其中没有键/值对。

和Key一样,一个表不能定义多次。这样做是无效的。

 

# DO NOT DO THIS

[fruit]
apple = "red"

[fruit]
orange = "orange"
# DO NOT DO THIS EITHER

[fruit]
apple = "red"

[fruit.apple]
texture = "smooth"

 

不鼓励乱序定义表。

 

# VALID BUT DISCOURAGED
[fruit.apple]
[animal]
[fruit.orange]
# RECOMMENDED
[fruit.apple]
[fruit.orange]
[animal]

顶层表,也称为根表(root table),从文档的开头开始,在第一个表头(或EOF)之前结束。与其他表不同,它是无名称的,不能被重新定位。

# Top-level table begins.
name = "Fido"
breed = "pug"

# Top-level table ends.
[owner]
name = "Regina Dogman"
member_since = 1999-08-04

带点“.” 的Key为最后一个键部分之前的每个键部分创建并定义一个表,前提是以前没有创建这样的表

fruit.apple.color = "red"
# Defines a table named fruit
# Defines a table named fruit.apple

fruit.apple.taste.sweet = true
# Defines a table named fruit.apple.taste
# fruit and fruit.apple were already created

 

由于表不能被多次定义,所以不允许使用[table]头文件重新定义这些表。同样,也不允许使用带点“.”的key重新定义已经在[table]形式中定义的表。然而,[table]表单可以用来在通过带“.”的Key定义的表中定义子表。

 

 

[fruit]
apple.color = "red"
apple.taste.sweet = true

# [fruit.apple]  # INVALID
# [fruit.apple.taste]  # INVALID

[fruit.apple.texture]  # you can add sub-tables
smooth = true

 

10)Inline Table

内联表为表示表提供了更紧凑的语法。它们对于分组数据特别有用,否则这些数据很快就会变得冗长。内联表完全定义在花括号:{和}中。在大括号内,可以出现零个或多个以逗号分隔的键/值对。键/值对的形式与标准表中的键/值对相同。允许所有值类型,包括内联表。

内联表的目的是显示在一行上。行内表的最后一个键/值对之后不允许使用终止逗号(也称为尾随逗号)。大括号之间不允许换行,除非它们在值内有效。即使如此,也不鼓励将内联表分成多个行。如果你发现自己被这种欲望所控制,这意味着你应该使用标准的表格。

 

name = { first = "Tom", last = "Preston-Werner" }
point = { x = 1, y = 2 }
animal = { type.name = "pug" }

上面的内联表与下面的标准表定义相同:

[name]
first = "Tom"
last = "Preston-Werner"

[point]
x = 1
y = 2

[animal]
type.name = "pug"

 

内联表是完全自包含的,并定义其中的所有键和子表。键和子表不能添加到大括号之外。

 

[product]
type = { name = "Nail" }
# type.edible = false  # INVALID

类似地,内联表也不能用于向已定义的表添加键或子表。

[product]
type.name = "Nail"
# type = { edible = false }  # INVALID

 

11) Array of Tables

该头文件的第一个实例定义数组及其第一个表元素,每个后续实例在该数组中创建并定义一个新的表元素。表按照遇到的顺序插入到数组中:

 

[[products]]
name = "Hammer"
sku = 738594937

[[products]]  # empty table within the array

[[products]]
name = "Nail"
sku = 284758393

color = "gray"

转换成的JSON格式如下:

{
  "products": [
    { "name": "Hammer", "sku": 738594937 },
    { },
    { "name": "Nail", "sku": 284758393, "color": "gray" }
  ]
}

 

对表数组的任何引用都指向数组中最近定义的表元素。这允许您在最近的表中定义子表,甚至表的子数组。

 

[[fruits]]
name = "apple"

[fruits.physical]  # subtable
color = "red"
shape = "round"

[[fruits.varieties]]  # nested array of tables
name = "red delicious"

[[fruits.varieties]]
name = "granny smith"


[[fruits]]
name = "banana"

[[fruits.varieties]]
name = "plantain"

 

转换成的格式如下:

 

{
  "fruits": [
    {
      "name": "apple",
      "physical": {
        "color": "red",
        "shape": "round"
      },
      "varieties": [
        { "name": "red delicious" },
        { "name": "granny smith" }
      ]
    },
    {
      "name": "banana",
      "varieties": [
        { "name": "plantain" }
      ]
    }
  ]
}

如果表或表的数组的父元素是数组元素,则必须在定义子元素之前已经定义了该元素。试图反转该顺序必须在解析时产生错误。

# INVALID TOML DOC
[fruit.physical]  # subtable, but to which parent element should it belong?
color = "red"
shape = "round"

[[fruit]]  # parser must throw an error upon discovering that "fruit" is
           # an array rather than a table
name = "apple"

 

尝试追加静态定义的数组,即使该数组为空,也必须在解析时产生错误。

# INVALID TOML DOC
fruits = []

[[fruits]] # Not allowed

 

试图定义与已经建立的数组同名的普通表,在解析时必须产生错误。尝试将普通表重新定义为数组也同样会产生解析时间错误。

# INVALID TOML DOC
[[fruits]]
name = "apple"

[[fruits.varieties]]
name = "red delicious"

# INVALID: This table conflicts with the previous array of tables
[fruits.varieties]
name = "granny smith"

[fruits.physical]
color = "red"
shape = "round"

# INVALID: This array of tables conflicts with the previous table
[[fruits.physical]]
color = "green"

您也可以在适当的地方使用内联表:

points = [ { x = 1, y = 2, z = 3 },
           { x = 7, y = 8, z = 9 },
           { x = 2, y = 4, z = 8 } ]

 

12)文件扩展名

TOML文件应该使用.toml扩展名

13)MIME Type

当在因特网上传输TOML文件时,适当的MIME类型是application/ toml

2、NSQD对TOML的使用

 

1) 第三方库:

 

或者是单独Get下来:

go get github.com/BurntSushi/toml

 

2) 创建example.toml

 

# This is a TOML document. Boom.

title = "TOML Example"

[owner]
name = "Tom Preston-Werner"
organization = "GitHub"
bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
dob = 1979-05-27T07:32:00Z # First class dates? Why not?

[database]
server = "192.168.1.1"
ports = [ 8001, 8001, 8002 ]
connection_max = 5000
enabled = true

[servers]

# You can indent as you please. Tabs or spaces. TOML don't care.
[servers.alpha]
ip = "10.0.0.1"
dc = "eqdc10"

[servers.beta]
ip = "10.0.0.2"
dc = "eqdc10"

[clients]
data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it

# Line breaks are OK when inside arrays
hosts = [
    "alpha",
    "omega"
]

2、分析这个toml文件,创建Table与Struct的对应关系:

TOML的Table

Go Struct

ConfigFile

tomlConfig

owner

ownerInfo

database

database

servers

server

servers.alpha

server

servers.beta

server

clients

clients

分析代码整个文件的tomlConfig struct应有的属性:

  • Title
  • ownerInfo
  • database
  • map[string]server,因为Table本身就是键值对的集合。
  • clients

clients struct应有的属性为:

  • Data [][]interface{}:二维数组
  • Hosts []string: 数组

server因为根据Key值进行区分,因此server struct应有的属性为:

  • IP string
  • DC string

 

 

整体的结构体的代码如下:

type tomlConfig struct {
   Title string
   Owner ownerInfo
   DB database `toml:"database"`
   Servers map[string]server
   Clients clients
}
type server struct {
   IP string
   DC string
}

type database struct {
   Server string
   Ports []int
   ConnMax int `toml:"connection_max"`
   Enabled bool
}

type clients struct {
   Data [][]interface{}
   Hosts []string
}
type ownerInfo struct {
   Name string
   Org string `toml:"organization"`
   Bio string
   DOB time.Time
}

 

解析

func main() {
   //配置文件结构体变量
   var config tomlConfig
   // mapping  TOML values to Go values
   if _, err := toml.DecodeFile("interview/toml/example.toml", &config); err != nil {
      fmt.Println(err)
      return
   }

   fmt.Printf("Title: %s\n",config.Title)
   fmt.Printf("Owner: %s (%s,%s),Born: %s\n",config.Owner.Name,config.Owner.Org,config.Owner.Bio,config.Owner.DOB)
   fmt.Printf("Database: %s %v (Max conn. %d),Enabled? %v\n",config.DB.Server,config.DB.Ports,config.DB.ConnMax,config.DB.Enabled)
   for serverName,server := range config.Servers {
      fmt.Printf("Server: %s,(%s,%s)\n",serverName,server.IP,server.DC)
   }
   fmt.Printf("Client data: %v\n",config.Clients.Data)
   fmt.Printf("Client hosts: %v\n",config.Clients.Hosts)
}

 

输出结果:

Title: TOML Example
Owner: Tom Preston-Werner (GitHub,GitHub Cofounder & CEO
Likes tater tots and beer.),Born: 1979-05-27 07:32:00 +0000 UTC
Database: 192.168.1.1 [8001 8001 8002] (Max conn. 5000),Enabled? true
Server: alpha,(10.0.0.1,eqdc10)
Server: beta,(10.0.0.2,eqdc10)
Client data: [[gamma delta] [1 2]]
Client hosts: [alpha omega]

主要是利用了以下方法:

// DecodeFile is just like Decode, except it will automatically read the
// contents of the file at `fpath` and decode it for you.
func DecodeFile(fpath string, v interface{}) (MetaData, error)

最终是利用反射进行文件的解析与映射,主要核心代码如下:

 

func Decode(data string, v interface{}) (MetaData, error) {
   rv := reflect.ValueOf(v)
   if rv.Kind() != reflect.Ptr {
      return MetaData{}, e("Decode of non-pointer %s", reflect.TypeOf(v))
   }
   if rv.IsNil() {
      return MetaData{}, e("Decode of nil %s", reflect.TypeOf(v))
   }
   p, err := parse(data)
   if err != nil {
      return MetaData{}, err
   }
   md := MetaData{
      p.mapping, p.types, p.ordered,
      make(map[string]bool, len(p.ordered)), nil,
   }
   return md, md.unify(p.mapping, indirect(rv))
}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DreamCatcher

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值