最近常用 Docker 跑伺服器做實驗,猛然發現 YAML 文件已是當代伺服器設定檔格式的業界標準,為自架伺服器、設定系統的必備技能,不會的話根本別想踏進 DevOps。

程式開發人員可能較熟悉 JSON,但來到 DevOps、容器管理的世界,YAML 的普及率更高。

YAML 跟 JSON 一樣,可以表達複雜結構,舉凡數字、布林、字串屬性、字串陣列,連複雜自訂物件組成的陣列也難不倒它,彈性與 JSON 不相上下。但與 JSON 最大的差異在於可讀性更高,不用留意 [ ] 對稱,還支援註解,比 JSON 更適合人員閱讀與撰寫。
(延伸閱讀:YAML 與 JSON 之間有何差異?)

JSONYAML
簡要說明一種可在軟體應用程式與服務之間交換結構化資料的資料序列化格式
優先考慮應用程式使用,而不是人類使用
一種可在軟體應用程式與服務之間交換結構化資料的資料序列化格式
優先考慮人類使用,而非應用程式使用
主要使用案例廣泛應用於各平台、語言、分散式軟體通訊、Web 應用程式、設計檔及 API大量應用於自動化、DevOps 和 IaC 工具和服務的組態檔案
可讀性可讀易讀
資料型態數字、布林值、Null、字串、陣列和物件透過包含序列、Scalar 和 Mapping 的巢狀物件集合支援各種型別
支援註解
支援資料物件做為值
版本控制可,但不易一眼看出不同版本間的差異可,版本之間的差異一目瞭然

表格來源

整理 YAML 格式重點:

  1. 使用縮排表示層級,一律用空白(不可用 Tab),縮排格數不拘,同層級空格數目相同即可
  2. 大小寫有別
  3. 若一個檔案包含多個文件,文件以 --- 起始、... 結束,但結束行可省略
  4. keyName: keyValue 呈現 Mapping/Dictionary (可對映成物件的屬性),keyValue 可巢狀套用 Dictionary/List
  5. 支援 Sequence/List 列舉多個項目,每個項目以 - 起首,項目可以是字串、數字、布林值,也可巢狀套用 Dictionary/List
  6. 可在任意位置加上 # 註解,能大幅提高文件可讀性

關於較完整的語法說明,我請 AI 生成一個火力展示範例,試著涵蓋所有 YAML 支援的資料形式,很適合當成 Cheatsheet。(範例經 ChatGPT/Caluse/Genimi/Grok 檢驗未發現錯誤,應可安心服用)

# --- Mappings (dictionaries), including nested mappings
person:
  name: Alice
  age: 30
  contact:
    email: alice@example.com
    phone: "123-456-7890"   # Nested mapping

# --- Sequences (lists): simple and of mappings
fruits:         # Simple sequence
  - Apple
  - Banana
  - Cherry

players:        # Sequence of mappings
  - name: Mark McGwire
    hr: 65
    avg: 0.278
  - name: Sammy Sosa
    hr: 63
    avg: 0.288

# --- Scalars: strings, numbers, booleans, nulls, special floats
scalars:
  string_implicit: Hello, YAML!         # Implicit string
  string_single:   'Single quoted'      # Single-quoted string
  string_double:   "Double quoted\nNewline"  # Double-quoted with escape
  int_decimal:     42                   # Decimal integer
  int_hex:         0x2A                 # Hexadecimal integer
  int_octal:       0o52                 # Octal integer
  float:           3.14                 # Float
  float_exp:       1.23e4               # Exponential
  bool_true:       true                 # Boolean true
  bool_false:      false                # Boolean false
  null_value:      null                 # Null
  null_tilde:      ~                    # Null (alternate)
  inf_value:       .inf                 # Positive infinity
  neg_inf_value:   -.inf                # Negative infinity
  nan_value:       .nan                 # Not a number

# --- Explicit type tags
explicit_types:
  force_string: !!str 123               # 123 as string
  force_int:    !!int "42"              # "42" as integer
  force_float:  !!float "3.14"          # "3.14" as float

# --- Multi-line strings: literal (|) and folded (>)
multiline:
  literal_block: |                      # Preserves line breaks
    This is a
    multi-line string.
    Every line break is kept.
  folded_block: >                       # Folds line breaks to spaces
    This is a
    folded
    multi-line string.

# --- Comments
# This is a single-line comment
another_key: value  # This is an inline comment
# Multiple
# line
# comments are just several single-line comments

至於要用 .NET 讀寫 YAML,目前還沒包含在 .NET 內建支援程式庫,較知名的兩套程式庫是 YamlDotNet 及 VYaml,YamlDotNet 較成熟使用者較多,依據西瓜偎大邊定理成為我的選擇,用它來做個簡單 YAML 讀寫練習。

using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;

class Program
{
    static void Main()
    {
        Action<string> printTitle = (title) =>
        {
            Console.ForegroundColor = ConsoleColor.Cyan;
            Console.WriteLine($"\n==== {title} ====");
            Console.ResetColor();
        };

        printTitle("YamlDotNet 序列化測試");
        var person = new Person
        {
            Name = "Jeffrey",
            Age = 25,
            Number = 123,
            Skills = new List<string> { "C#", "JavaScript" },
            Equipments = new List<Equipment>
            {
                new Equipment { Name = "Laptop", Weight = 2 },
                new Equipment { Name = "Phone", Weight = 0.5f }
            }
        };

        var serializer = new SerializerBuilder()
            .WithNamingConvention(CamelCaseNamingConvention.Instance)
            .Build();

        string yaml = serializer.Serialize(person);
        Console.Write(yaml);

        // 反序列化為強型別物件
        printTitle("反序列化為強型別物件");
        var deserializer = new DeserializerBuilder()
            .WithNamingConvention(CamelCaseNamingConvention.Instance)
            .Build();
        var deserializedPerson = deserializer.Deserialize<Person>(yaml);
        Console.WriteLine($"Name: {deserializedPerson.Name}");
        Console.WriteLine($" - Number: {deserializedPerson.Number}({deserializedPerson.Number.GetType().Name})");
        Console.WriteLine($" - Skilles: {string.Join(", ", deserializedPerson.Skills)}");

        // 反序列化為動態物件
        printTitle("反序列化為動態物件");
        var dynamicObject = deserializer.Deserialize<dynamic>(yaml);
        Console.WriteLine($"Name: {dynamicObject["name"]}");
        Console.WriteLine($" - Number: {dynamicObject["number"]}({dynamicObject["number"].GetType().Name})");
        List<dynamic> equipments = dynamicObject["inventory"];
        foreach (var item in equipments)
        {
            Console.WriteLine($" - Equipment: {item["name"]} (Weight: {item["weight"]})");
        }
    }
}

public class Person
{
    public required string Name { get; set; }
    public int Age { get; set; }
    public decimal Number { get; set; }
    public required List<string> Skills { get; set; }
    [YamlMember(Alias = "inventory", ApplyNamingConventions = false)]
    public required List<Equipment> Equipments { get; set; }
}
public class Equipment
{
    public required string Name { get; set; }
    public float Weight { get; set; }
}

Docker experiments highlight YAML as a DevOps standard for server configuration, offering readability and features like comments, unlike JSON. The blog compares YAML and JSON and demonstrates YAML serialization/deserialization with YamlDotNet in .NET.


Comments

Be the first to post a comment

Post a comment