YAML 语法简介与 C# 操作示例

〇、简介

YAML(Yet Another Markup Language)另一种标记语言。

YAML 是一种较为人性化的数据序列化语言,可以配合目前大多数编程语言使用。YAML 的语法比较简洁直观,特点是使用空格来表达层次结构,其最大优势在于数据结构方面的表达,所以 YAML 更多应用于编写配置文件,其文件一般以 .yml 为后缀

特点:

  • 易于阅读:YAML 使用缩进和比较简洁的语法来表示数据结构,使得它比许多其他数据格式更容易阅读和理解。
  • 数据结构友好:YAML 天然支持标量(如字符串、整数、浮点数)、列表(数组)和映射(字典)等数据结构。
  • 无类型标签:YAML 通过上下文来推断值的类型,不需要显式的类型标签
  • 可交互:YAML 可以在不同的编程语言之间进行交互,因为它有广泛的语言支持
  • 表达能力强:YAML 可以表示复杂的数据结构,并且可以通过锚点和别名来重用数据
  • 可伸缩性:YAML 可以很容易地扩展到新的数据类型,而不需要改变现有的解析器。

YAML 的使用场景包括但不限于:应用程序的配置、数据交换格式、文档撰写、自动化脚本、云计算和服务编排等等。

一、YAML 语法

1.1 基本语法

  • 大小写敏感。
  • 使用缩进表示层级关系。
  • 缩进时不允许使用Tab键,只允许使用空格。
  • 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可。
# YAML one:   two: 2   three:     four: 4     five: 5
// 转成 JSON 后的格式 "one": {   "two": 2,   "three": {     "four": 4,     "five": 5    } }
  • 用 # 标识注释,且只能单行
# 我是一行注释 # 我是另一行注释
  •  一个 YAML 文件可以包含多个文档

每个文档均以“---”三个横杠开始,如果一个文件中仅一个文档,则可省略

每个文档并不必须使用结束符“...”来表示结束,但是对于网络传输或者流来说,作为明确结束的符号,有利于软件处理。(例如,不需要知道流关闭就能知道文档结束)

--- # 这是第一份文档内容 one: 1 # 其他内容... ...  --- # 这是第二份文档内容 two: 2 # 其他内容...

1.2 数据结构与类型

1.2.1 对象 Mapping

标识以键值对(key: value)形式出现的数据。

  • 格式

在键和值中间加入标识,冒号+空格(: )

# YAML key: value
// JSON {     "key": "value" }
  • 多层嵌套数据

缩进表示层级关系

# YAML key:   child-key1: value1   child-key2: value2
// JSON {     "key": {       "child-key1": "value1",       "child-key2": "value2",     } }
  • 用一对 {} 花括号包裹,表示一个键值表

键值对之间用逗号+空格(, )分隔,类似 JSON。

# YAML key: { child-key1: value1, child-key2: value2 }
// JSON {     "key": {         "child-key1": "value1",         "child-key2": "value2"     } }
  • 问号+空格(? )表示复杂的键

键是一个列表或键值表时,就需要使用本符号来标记。

# 使用一个列表作为键 [blue, reg, green]: Color # 等价于 ? - blue   - reg   - gree : Color
// JSON {     "blue,reg,gree": "Color" }
  • 多种组合表示

每个结构都可以嵌套组成复杂的表示结构。

# YAML div:   - border: {color: red, width: 2px}   - background: {color: green}   - padding: [0, 10px, 0, 10px]
// JSON {     "div": [         {             "border": {                 "color": "red",                 "width": "2px"             }         },         {             "background": {                 "color": "green"             }         },         {             "padding": [0, "10px", 0, "10px"]         }     ] }
# YAML items:   - item: cpu     model: i3     price: ¥800.00   - item: HD     model: WD     price: ¥450.00
// JSON {     "items": [         {             "item": "cpu",             "model": "i3",             "price": "¥800.00"         },         {             "item": "HD",             "model": "WD",             "price": "¥450.00"         }     ] }

1.2.2 数组 Sequence

  • 横线+空格(- )开头的数据组成一个数组
# YAML 区块格式(Block Format) values:  - value1  - value2  - value3 # YAML 内联格式(Inline Format) values: [value1, value2, value3]
// JSON {     "values": [         "value1",         "value2",         "value3"     ] }
  • 多维数组
# YAML values:   -     - value1     - value2   -     - value3     - value4
// JSON {     "values": [         [             "value1",             "value2"         ],         [             "value3",             "value4"         ]     ] }
  • 数组组合
# YAML - [blue, red, green]     # 列表项本身也是一个列表 - [Age, Bag] - site: {osc:www.oschina.net, baidu: www.baidu.com}  # 这里是同 键值表 组合表示
// JSON [     [         "blue",         "red",         "green"     ],     [         "Age",         "Bag"     ],     {         "site": {             "osc:www.oschina.net": null,             "baidu": "www.baidu.com"         }     } ]
  • 复合结构
# YAML  languages:  - Ruby  - Perl  - Python websites:  YAML: yaml.org  Ruby: ruby-lang.org  Python: python.org  Perl: use.perl.org
// JSON {     "languages": [         "Ruby",         "Perl",         "Python"     ],     "websites": {         "YAML": "yaml.org",         "Ruby": "ruby-lang.org",         "Python": "python.org",         "Perl": "use.perl.org"     } }

1.2.3 标量 Scalars 基本数据类型-str、bool、int、float、null、datetime...

本章节包含以下部分简介:字符串 String、布尔值 boolean、整数 Integer、浮点数 Float、空 Null、日期时间 datetime、类型强制转换等。

  • 字符串(string、str)

字符串是最常见,也是最复杂的一种数据类型。

字符串一般不需要用引号包裹,但是如果字符串中使用了反斜杠“”开头的转义字符就必须使用引号包裹。

# YAML strings:   - Hello without quote # 不用引号包裹   - Hello    world # 拆成多行后会自动在中间添加空格   - 'Hello with single quotes' # 单引号包裹   - "Hello with double quotes" # 双引号包裹   - "I am fine. u263A" # 使用双引号包裹时支持 Unicode 编码   - "x0dx0a is rn" # 使用双引号包裹时还支持 Hex 编码   - 'He said: "Hello!"' # 单双引号支持嵌套"
// JSON {     "strings": [         "Hello without quote",         "Hello world",         "Hello with single quotes",         "Hello with double quotes",         "I am fine. ☺",         "rn is rn",         "He said: "Hello!""     ] }

用竖线符“ | ”来表示保留换行(Newlines preserved)。

每行的前边缩进后边的空白会被去掉,而额外的缩进和行后的空格会被保留。

# YAML lines: |   我是第一行       我是第二行     我是吴彦祖       我是第四行   我是第五行
// JSON {     "lines": "我是第一行    n我是第二行n  我是吴彦祖n    我是第四行n我是第五行n" }

用右尖括号“ > ”来表示折叠换行(Newlines folded)。

只有空白行才会被识别为换行,原来的换行符都会被转换成空格。最后也会以换行符结束。

# YAML lines: >   我是第一行   我也是第一行   我仍是第一行   我依旧是第一行    我是第二行   这么巧我也是第二行
// JSON {     "lines": "我是第一行 我也是第一行 我仍是第一行 我依旧是第一行n我是第二行 这么巧我也是第二行n" }
  • 布尔值(Boolean、bool)

经测试,只有全部大写、全部小写、首字母大写这三种情况,可以自动识别为布尔值。其他情况均转成字符串,如下:

# YAML boolean:   - true   - True   - TRUE   - TRue   - false   - False   - FALSE   - FAlse
// JSON {     "boolean": [         true,         true,         true,         "TRue",         false,         false,         false,         "FAlse"     ] }
  • 整数(Integer、int)

YAML 允许二进制的整数,但前边需要带上标识:‘0b’。

# YAML int:   - 666   - 0b0010_1110  # 二进制表示
//JSON {     "int": [         666,         46     ] }
  • 浮点数(Floating-point、float)

允许使用科学计数法,如下代码:

# YAML float:   - 3.14   - 6.8523015e+5 # 使用科学计数法
// JSON {     "float": [         3.14,         685230.15     ] }
  • 空(Null)
# YAML nulls:   - null   - Null   - ~   -  # 未指定值
// JSON {     "nulls": [         null,         null,         null,         null     ] }
  • 日期时间(date、datetime)

没有 +8 小时的标记时,默认就是协调世界时(UTC),也就是标准时间,转换成 JSON 都是按照协调世界时的格式,如下代码:

# YAML dates:   - 2024-03-05 # 协调世界时(UTC)   - 2024-03-05T20:00:00 # 协调世界时(UTC)   - 2024-03-05T20:00:00+08:00 # +8 小时就是北京时间   - 2024-03-05T20:00:00.10+08:00   - 2024-03-05 20:00:00.10 +8
// JSON {     "dates": [         "2024-03-05T00:00:00.000Z",         "2024-03-05T20:00:00.000Z",         "2024-03-05T12:00:00.000Z",         "2024-03-05T12:00:00.100Z",         "2024-03-05T12:00:00.100Z"     ] }
  • 类型转换(双叹号:!!)

YAML 支持使用严格类型标签:“!!”(格式:双感叹号+目标类型),来强制转换类型,如下代码:

# YAML strings_convert:   - !!float '666' # 字符串转浮点数   - '666'   - !!str 666 # 整数转为字符串   - !!str 666.66 # 浮点数转为字符串   - !!str true # 布尔值转为字符串   - !!bool 'true' # 字符串转布尔值
// JSON {     "strings_convert": [         666,         "666",         "666",         "666.66",         "true",         true     ] }

1.3 数据重用和合并(&、*、<<)

为了保持内容的简洁,避免过多重复的定义,YAML 提供了由锚点标签“&”和引用标签“*”组成的语法,利用这套语法可以快速引用相同的一些数据。如下代码:

# YAML a: &anchor # 设置锚点   one: 1   two: 2   three: 3 b: *anchor # 引用锚点
// JSON {     "a": {         "one": 1,         "two": 2,         "three": 3     },     "b": {         "one": 1,         "two": 2,         "three": 3     } }

配合合并标签“<<”使用可以与任意数据进行合并,可以把这套操作,类比为面向对象语言中的继承。如下代码:

# YAML human: &base # 添加名为 base 的锚点     body: 1     hair: 999 singer:     <<: *base # 引用 base 锚点,实例化时会自动展开     skill: sing # 添加额外的属性 programer:     <<: *base # 引用 base 锚点,实例化时会自动展开     hair: 6 # 覆写 base 中的属性     skill: code # 添加额外的属性
// JSON {     "human": {         "body": 1,         "hair": 999     },     "singer": {         "body": 1,         "hair": 999,         "skill": "sing"     },     "programer": {         "body": 1,         "hair": 6,         "skill": "code"     } }

参考:https://zhuanlan.zhihu.com/p/145173920  https://www.jianshu.com/p/413576dc837e   https://zhuanlan.zhihu.com/p/75067291   https://ruanyifeng.com/blog/2016/07/yaml.html

测试 yaml 转 json:https://www.lddgo.net/convert/yaml-to-json              

二、C# 读取 YAML 配置文件示例

知道了 YAML 的特点和语法,下面就来上马试试看吧。

2.1 安装必要的动态库 YamlDotNet

首先通过 NuGet 安装依赖:YamlDotNet,这个动态库是比较专门为 C# 操作 YAML 定制的,官方支持非常好。

YAML 语法简介与 C# 操作示例

2.2 YAML 示例文件

如下文件中,一个主节点中包含两个子节点:

assetBundles: # 主节点 - name: myname  # 子节点-1   size: 123   variant: ''   version: 1   md5: sdhbuuhkhekghddfgkshjgn   dependencies: []   local: false   assets:    - Assets1/Birthday_FUMSIAMO.png   - Assets1/Partner_lock.png   - Assets1/shop_01.png - name: myname2  # 子节点-2   size: 1232   variant: ''   version: 2   md5: sdhbuuhkhekghddfgkshjgn   dependencies: ["1","2"]   local: false   assets:    - Assets2/Birthday_FUMSIAMO.png   - Assets2/Partner_lock.png   - Assets2/shop_01.png

2.2 实际的操作代码

2.2.1 先看测试代码和测试结果

// 必要的引用 using System; using System.Text; using System.Collections.Generic; using System.IO; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions;
// 测试一下 【读取、修改、保存】 static void Main(string[] args) {     var serializer = new SerializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build();     var deserializer = new DeserializerBuilder().WithNamingConvention(UnderscoredNamingConvention.Instance).Build();     var ymlFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "asset_table.yml");     if (File.Exists(ymlFile))     {         var ymlContent = File.ReadAllText(ymlFile); // 读取 yaml 文件内容         var buildConfig = deserializer.Deserialize<BuildConfigFile>(ymlContent); // 序列化         if (buildConfig != null)         {             foreach (var item in buildConfig.assetBundles)             {                 // 获取配置内容并修改                 if(item.name== "myname")                     item.name = "myname_new";                 else if(item.name=="myname2")                     item.name = "myname2_new";             }             // 序列化成新的 yaml 文本并保存             var newYamlContent = serializer.Serialize(buildConfig);             File.WriteAllText(ymlFile, newYamlContent);         }     } }

测试结果:

注意:修改后的文件存在调试文件夹中:binDebugnet7.0asset_table.yml,在项目中的 asset_table.yml 文件依然是没有变化。

YAML 语法简介与 C# 操作示例

YAML 语法简介与 C# 操作示例

2.2.2 根据示例 YAML 文件输出基本数据模型

public class BuildConfigFile {     public class AssetBundleItem     {         [YamlMember(Alias = "name")]         public string name { get; set; }          [YamlMember(Alias = "size")]         public long size { get; set; }          [YamlMember(Alias = "variant")]         public string variant { get; set; }          [YamlMember(Alias = "version")]         public long version { get; set; }          [YamlMember(Alias = "md5")]         public string md5 { get; set; }          [YamlMember(Alias = "md5bytes")]         public string md5bytes { get; set; }          [YamlMember(Alias = "dependencies")]         public string[] dependencies { get; set; }          [YamlMember(Alias = "local")]         public bool local { get; set; }          [YamlMember(Alias = "assets")]         public string[] assets { get; set; }     }      [YamlMember(Alias = "assetBundles")]     public List<AssetBundleItem> assetBundles { get; set; } }

2.2.3 最后是压轴的 YAML 操作类

public static class YamlHelper {     private static ISerializer _serializer;     private static IDeserializer _deserializer;     static YamlHelper()     {         _serializer = new SerializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build();         _deserializer = new DeserializerBuilder().WithNamingConvention(UnderscoredNamingConvention.Instance).Build();     }     public static string Serialize(object target)     {         return _serializer.Serialize(target);     }     public static void SerializeToFile(object target, string filePath)     {         var content = Serialize(target);         File.WriteAllText(filePath, content, Encoding.UTF8);     }     public static T Deserialize<T>(string yaml)     {         return _deserializer.Deserialize<T>(yaml);     }     public static T DeserializeFromFile<T>(string filePath)     {         var yaml = File.ReadAllText(filePath, Encoding.UTF8);         return Deserialize<T>(yaml);     } }

2.3 遇到的一个报错“Property 'assetBundles' not found on type 'TimerDispose.BuildConfigFile'.”

第一次运行没问题,但重复运行程序时,就会报出这个错误:

YAML 语法简介与 C# 操作示例

原因:是由于生成新的 YAML 文件中主节点由原本的 asset_bundles 更新成 assetBundles。数据模型中设置为 Alias = "asset_bundles",因此无法读取成功。

因此,这个报错的主要意思就是,根据设定好的数据模型,因字段对应不上而识别失败。

参考:https://blog.csdn.net/rjcql/article/details/134341930

发表评论

评论已关闭。

相关文章