Go解析yaml和yml文件
文章目录
1、yaml概述
yaml一般作为配置文件的语言,类似json、xml等,但和Windows中init配置文件的功能较为相似,但更好理解,我第一次接触是在clash软件中,通过yaml文件导入相关网络数据。
以下来自维基百科的基础介绍:
YAML(/ˈjæməl/,尾音类似camel骆驼)是一个可读性高,用来表达资料序列化的格式。YAML参考了其他多种语言,包括:C语言、Python、Perl,并从XML、电子邮件的数据格式(RFC 2822)中获得灵感。Clark Evans在2001年首次发表了这种语言[1],另外Ingy döt Net与Oren Ben-Kiki也是这语言的共同设计者[2]。目前已经有数种编程语言或脚本语言支持(或者说解析)这种语言。
YAML是"YAML Ain’t a Markup Language"(YAML不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:“Yet Another Markup Language”(仍是一种标记语言)[3],但为了强调这种语言以数据做为中心,而不是以标记语言为重点,而用反向缩略语重命名。(这让我想起了Linux is not Unix)
2、功能
YAML的语法和其他高级语言类似,并且可以简单表达清单、散列表,标量等资料形态。[4]它使用空白符号缩进和大量依赖外观的特色,特别适合用来表达或编辑数据结构、各种配置文件、倾印调试内容、文件大纲(例如:许多电子邮件标题格式和YAML非常接近)
。尽管它比较适合用来表达层次结构式(hierarchical model)的数据结构,不过也有精致的语法可以表示关系性(relational model)的资料。[5]由于YAML使用空白字符和分行来分隔资料,使得它特别适合用grep/Python/Perl/Ruby操作。其让人最容易上手的特色是巧妙避开各种封闭符号,如:引号、各种括号等,这些符号在嵌套结构时会变得复杂而难以辨认。
3、示例
简单的文件:
---
receipt: Oz-Ware Purchase Invoice
date: 2012-08-06
customer:
given: Dorothy
family: Gale
items:
- part_no: A4786
descrip: Water Bucket (Filled)
price: 1.47
quantity: 4
- part_no: E1628
descrip: High Heeled "Ruby" Slippers
size: 8
price: 133.7
quantity: 1
bill-to: &id001
street: |
123 Tornado Alley
Suite 16
city: East Centerville
state: KS
ship-to: *id001
specialDelivery: >
Follow the Yellow Brick
Road to the Emerald City.
Pay no attention to the
man behind the curtain.
...
注意在YAML中,字符串不一定要用双引号标示。另外,在缩进中空白字符的数目并不是非常重要,只要相同层次结构的元素左侧对齐就可以了(不过不能使用TAB字符)。这个文件的顶层由七个键值组成:其中一个键值"items",是两个元素构成的数组(或称清单),这清单中的两个元素同时也是包含了四个键值的散列表。文件中重复的部分用这个方法处理:使用锚点(&)和引用(*)标签将"bill-to"散列表的内容复制到"ship-to"散列表。也可以在文件中加入选择性的空行,以增加可读性。在一个文件中,可同时包含多个文件,并用"—“分隔。选择性的符号”…"可以用来表示文件结尾(在利用流的通信中,这非常有用,可以在不关闭流的情况下,发送结束信号)。
4、语言的构成元素
YAML提供缩进/区块以及内置(inline)两种格式,来表示清单和散列表。以下展示几种YAML的基本原件。
(1)、清单(数组)
习惯上清单比较常用区块格式(block format)表示,也就是用短杠+空白字符作为起始。
--- # 最喜愛的電影
- Casablanca
- North by Northwest
- Notorious
另外还有一种内置格式(inline format)可以选择──用方括号围住,并用逗号+空白区隔(类似JSON的语法)。
--- # 購物清單
[milk, pumpkin pie, eggs, juice]
(2)、关系数组
键值和资料由冒号及空白字符分开。区块形式(常使用于YAML数据文档中)使用缩进和换行符分隔key: value对。内置形式(常使用于YAML数据流中)在大括号中使用逗号+空白字符分隔key: value对。
--- # 區塊形式
name: John Smith
age: 33
--- # 內置形式
{name: John Smith, age: 33}
(3)、区块的字符
再次强调,字符串不需要包在引号之内。
有两种语法可以书写多行文字(multi-line strings),一种为保留换行(使用|字符),另一种为折叠换行(使用>字符)。两种语法在其特殊字符之后都必须接着换行。
(4)、保留换行(Newlines preserved)
data: | # 譯者注:這是一首著名的五行民謠(limerick)
There once was a man from Darjeeling # 這裡曾有一個人來自大吉嶺
Who got on a bus bound for Ealing # 他搭上一班往伊靈的公車
It said on the door # 門上這麼說的
"Please don't spit on the floor" # "請勿在地上吐痰"
So he carefully spat on the ceiling # 所以他小心翼翼的吐在天花板上
根据默认,每行开头的缩进(以首行为基准)和行末空白会被去除,而不同的缩进会保留差异。
(5)、折叠换行(Newlines folded)
data: >
Wrapped text # 摺疊的文字
will be folded # 將會被收
into a single # 進單一一個
paragraph # 段落
Blank lines denote # 空白的行代表
paragraph breaks # 段落之間的區隔
和保留换行不同的是,只有空白行才视为换行,原本的换行字符会被转换成空白字符,而行首缩进会被去除。
(6)、层次结构化的元素
于清单中使用散列表
- {name: John Smith, age: 33}
- name: Mary Smith
age: 27
于散列表中使用清单
men: [John Smith, Bill Jones]
women:
- Mary Smith
- Susan Williams
5、yml和yaml
文件扩展名对文件内容没有任何影响。您可以将YAML内容保存在具有任何扩展名的文件中:.yml,.yaml或其他任何扩展名。
YAML FAQ建议您优先使用.yaml而不是.yml,但是由于历史原因,许多Windows程序员仍然害怕使用具有三个以上字符的扩展名,因此选择使用.yml。
因此,真正重要的是文件内部的内容,而不是文件扩展名。
6、常见解析库
可以查看Wiki的推荐:https://zh.wikipedia.org/wiki/YAML
7、Go解析库使用示例
如果你是纯yaml的解析可以使用推荐较多的yaml.v2:https://github.com/go-yaml/yaml
如果可能还有json等格式文件的话则可以使用:https://github.com/jinzhu/configor
我们项目上目前是使用的下面这个,这里也以该库做一个简单的示例。
package main
import (
"fmt"
"github.com/jinzhu/configor"
)
var Config = struct {
AppName string `default:"app name"`
DB struct {
Name string
User string `default:"root"`
Password string `required:"true" env:"DBPassword"`
Port uint `default:"3306"`
}
Contacts []struct {
Name string
Email string `required:"true"`
}
}{}
func main() {
configor.Load(&Config, "config.yaml")
fmt.Printf("config: %#v\n", Config)
fmt.Println("config: ", Config)
}
结果:
config: struct { AppName string "default:\"app name\""; DB struct { Name string; User string "default:\"root\""; Password string "required:\"true\" env:\"DBPassword\""; Port uint "default:\"3306\"" }; Contacts []struct { Name string; Email string "required:\"true\"" } }{AppName:"test", DB:struct { Name string; User string "default:\"root\""; Password string "required:\"true\" env:\"DBPassword\""; Port uint "default:\"3306\"" }{Name:"test", User:"test", Password:"test", Port:0x4d2}, Contacts:[]struct { Name string; Email string "required:\"true\"" }{struct { Name string; Email string "required:\"true\"" }{Name:"i test", Email:"test@test.com"}}}
config: {test {test test test 1234} [{i test test@test.com}]}