目录
一、简介
YAML(YAML Ain't Markup Language)是一种数据序列化标准,广泛用于配置文件、数据交换和存储。YAML的设计目标是易于阅读和编写,同时也易于机器解析和生成。以下是YAML的一些关键特点和用途的介绍:
-
简洁性:yaml 使用缩进和简洁的语法来表示数据结构,这使得它比 XML 或 Json 等格式更易于阅读和编写。
-
数据类型支持:yaml 支持多种数据类型,包括标量(如字符串、整数、浮点数)、序列(列表)、映射(字典)以及复杂的数据结构。
-
跨语言支持:yaml 可以被多种编程语言解析和生成,包括 Python、Ruby、Java、JavaScript 等,这使得它在不同系统间的数据交换中非常有用。
-
配置文件:yaml 常用于编写配置文件,因为它的人类可读性和灵活性使得配置管理变得简单。例如,Docker 和Kubernetes 都使用 yaml 来定义容器和集群的配置。
-
文档分离:yaml 支持文档分离,即一个文件中可以包含多个独立的 yaml 文档,每个文档由三个破折号(---)分隔。
-
引用和锚点:YAML允许使用引用和锚点来避免数据重复,这可以提高数据的可维护性和减少错误。
-
兼容性:YAML可以与JSON相互转换,这意味着YAML文件可以很容易地转换为JSON格式,反之亦然。
yaml 有以下基本规则:
1. 大小写敏感。
2. 使用缩进表示层级关系,缩进时不允许使用Tab键,只允许使用空格键,缩进的空格数不重要,只要元素左侧能对齐就可。
3.空格,在 value 的值前边必须有空格,否则读取时会报错,比如:name: jack,而不是 name:jack,键值对的冒号后面至少有一个空格。
4.引号,value 的值如果是字符串,可以用引号也可以不用,单引号和双引号都可以。
5. yaml 兼容 Json 的语法,插件 YamlDotNet 同样也可以读写 Json。
yaml 在读取性能上不如 Json,但在需要手写配置文件的时候,yaml 要比 Json 方便不少,不用添加那么多对称的大括号(花括号)和中括号(方括号),读起来也更简单易懂,并且在多个编程语言中都是通用的。
文章的第二部分:
二、yaml 的符号
这里只介绍几个常用的符号,这不是 yaml 所有运算符,有需要的可以参考官方文档 The Official YAML Web Site
在使用 C# 进行读写时,需要安装一个插件 YamlDotNet
YamlDotNet 是开源的,github 可以找到对应的源码。
1.冒号
( : )冒号用于键值对的分隔,冒号后必须跟一个空格
username: admin
password: 123456
C# 实例:
using System;
using System.Collections.Generic;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace yaml_test
{
internal class Program
{
static void Main(string[] args)
{
string yaml =
@"
name: zhangsan
";
var deserializer = new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.Build();
var data = deserializer.Deserialize<Dictionary<string, object>>(yaml);
string name = data["name"] as string;
Console.WriteLine("名字:{0}", name);
Console.ReadKey();
}
}
}
运行:
2.短横杆
( - )短横杆用于表示列表(数组)的开始
fruits:
- Apple
- Banana
- Cherry
C# 实例:
using System;
using System.Collections.Generic;
using System.Linq;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace yaml_test
{
internal class Program
{
static void Main(string[] args)
{
string yaml =
@"
fruits:
- Apple
- Banana
- Cherry
";
var deserializer = new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.Build();
var data = deserializer.Deserialize<Dictionary<string, object>>(yaml);
if (data.ContainsKey("fruits") && data["fruits"] is IList<object> fruitsList)
{
string[] fruitsArray = fruitsList.Cast<string>().ToArray();
Console.WriteLine(string.Join(", ", fruitsArray));
}
Console.ReadKey();
}
}
}
运行:
3.文档分隔符
使用三个连字符(---)表示一个YAML文档的开始,可选地使用三个点(...)表示文档的结束(但在实践中通常省略)。
---
# 第一个文档
username: admin
---
# 第二个文档
name: Project A
description: A new project
...
4.保留换行符
| 和 > 用于处理多行字符串,其中 | 保留字符串的换行符,而 > 将换行符转换为空格。
poem: |
胜日寻芳泗水滨,
无边光景一时新。
等闲识得东风面,
万紫千红总是春。
description: >
风和日丽之时游览在泗水之滨,
无边无际的风光让人耳目一新。
谁都可以看出春的面貌,万紫千红,
到处都是百花开放的春景。
C# 实例:
using System;
using System.Collections.Generic;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace yaml_test
{
internal class Program
{
static void Main(string[] args)
{
string yaml =
@"
poem: |
胜日寻芳泗水滨,
无边光景一时新。
等闲识得东风面,
万紫千红总是春。
description: >
风和日丽之时游览在泗水之滨,
无边无际的风光让人耳目一新。
谁都可以看出春的面貌,万紫千红,
到处都是百花开放的春景。
";
var deserializer = new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.Build();
var data = deserializer.Deserialize<Dictionary<string, object>>(yaml);
string poem = data["poem"] as string;
Console.WriteLine(poem);
Console.WriteLine("===============");
string description = data["description"] as string;
Console.WriteLine(description);
Console.ReadKey();
}
}
}
运行:
5.注释
使用井号(#)进行单行注释。
#这是一个注释
username: admin
password: 123456 #密码
6.锚点
如果你有一个值需要在多个地方重复使用,可以使用 yaml 的锚点和别名功能来避免重复写入。锚点(&
)用于标记一个节点,而别名(*
)用于引用这个锚点。
比如下面的案例中,ip 这个值需要在很多的地方用到,我们就可以使用 &ip 来标记一个锚点,然后使用 *ip 来引用。
ipAddress: &ip '192.168.1.1'
server1:
name: 'Server 1'
ip: *ip
server2:
name: 'Server 2'
ip: *ip
server3:
name: 'Server 3'
ip: *ip
C# 实例:
using System;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace yaml_test
{
internal class Program
{
static void Main(string[] args)
{
string yaml =
@"
ipAddress: &ip '192.168.1.1'
server1:
name: 'Server 1'
ip: *ip
server2:
name: 'Server 2'
ip: *ip
server3:
name: 'Server 3'
ip: *ip
";
var deserializer = new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.Build();
var config = deserializer.Deserialize<ServersConfig>(yaml);
Console.WriteLine($"Server 1: Name = {config.Server1.Name}, IP = {config.Server1.Ip}");
Console.WriteLine($"Server 2: Name = {config.Server2.Name}, IP = {config.Server2.Ip}");
Console.WriteLine($"Server 3: Name = {config.Server3.Name}, IP = {config.Server3.Ip}");
Console.ReadKey();
}
}
}
public class Server
{
public string Name { get; set; }
public string Ip { get; set; }
}
public class ServersConfig
{
public string IpAddress { get; set; }
public Server Server1 { get; set; }
public Server Server2 { get; set; }
public Server Server3 { get; set; }
}
运行:
由于 yaml 不支持字符串的拼接,只能在编程语言中自行合并,如果确实有这个需求的话,只能在 yaml 中使用编程语言更加方便的读取方式,比如数组,yaml 数组中也是支持使用瞄点的。
ipAddress: &ip 'http://192.168.1.1'
server1:
name: 'Server 1'
ip: [*ip,'/biz/getVersion']
server2:
name: 'Server 2'
ip: [*ip, '/biz/getName']
server3:
name: 'Server 3'
ip: [*ip, '/biz/getAge']
C# 实例:
using System;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace yaml_test
{
internal class Program
{
static void Main(string[] args)
{
string yaml =
@"
ipAddress: &ip 'http://192.168.1.1'
server1:
name: 'Server 1'
ip: [*ip,'/biz/getVersion']
server2:
name: 'Server 2'
ip: [*ip, '/biz/getName']
server3:
name: 'Server 3'
ip: [*ip, '/biz/getAge']
";
var deserializer = new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.Build();
var config = deserializer.Deserialize<ServersConfig>(yaml);
Console.WriteLine($"Server 1: Name = {config.Server1.Name}, IP = {string.Join("", config.Server1.Ip)}");
Console.WriteLine($"Server 2: Name = {config.Server2.Name}, IP = {string.Join("", config.Server2.Ip)}");
Console.WriteLine($"Server 3: Name = {config.Server3.Name}, IP = {string.Join("", config.Server3.Ip)}");
Console.ReadKey();
}
}
}
public class Server
{
public string Name { get; set; }
public string[] Ip { get; set; }
}
public class ServersConfig
{
public string IpAddress { get; set; }
public Server Server1 { get; set; }
public Server Server2 { get; set; }
public Server Server3 { get; set; }
}
运行:
7.NULL值
可以用 ~
或 null
来表示空值。例如:
value: ~
another_value: null
C# 实例:
using System;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace yaml_test
{
internal class Program
{
static void Main(string[] args)
{
string yaml =
@"
value: ~
another_value: null
";
var deserializer = new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.Build();
var data = deserializer.Deserialize<dynamic>(yaml);
string value = data["value"];
string another_value = data["another_value"];
Console.WriteLine("value:{0}", value);
Console.WriteLine("another_value:{0}", another_value);
Console.ReadKey();
}
}
}
运行:
断点看看
8.合并
<< 是一个合并的语法,用于将一个映射(字典)的内容合并到另一个映射中。这种语法通常与锚和别名一起使用,以便重用和继承。
defaults: &defaults
name: John
age: 30
person1:
<<: *defaults # 合并 defaults 的内容
location: USA
person2:
<<: *defaults # 合并 defaults 的内容
location: Canada
合并后的结构将是:
person1:
name: John
age: 30
location: USA
person2:
name: John
age: 30
location: Canada
由于插件 YamlDotNet 目前并不支持合并语法(<<),所以这里就不做演示了,但它不代表所有编程语言都不支持合并语法,在有些编程语言中它是支持的,比如:Python
文章的第二部: