varch/doc/yaml.md
2025-05-10 21:28:42 +08:00

18 KiB
Raw Permalink Blame History

介绍

什么是YAML文件

YAMLYAML Ain't Markup Language是一种人类可读的数据序列化格式专注于简洁表达结构化数据。它广泛用于配置文件、数据交换和复杂数据结构的描述如Kubernetes、Ansible等工具的配置而非传统电子表格类的表格数据存储。

YAML文件的特点

  • 可读性高

使用缩进和符号(如-:表示层级关系类似自然语言的格式比JSON/XML更易阅读。

user:
  name: Alice
  age: 30
  hobbies:
    - reading
    - hiking
  • 支持复杂数据结构
    可表示标量(字符串、数字)、列表、字典等类型,支持嵌套和多行文本(通过|>)。

  • 跨平台兼容
    纯文本格式,所有操作系统和编程语言均可解析。

  • 与编程语言无缝集成
    大多数语言Python、Java、Go等提供原生或第三方库如PyYAML支持YAML解析。

YAML的用途

  1. 配置文件(核心用途)
  • 软件配置如Docker Compose、GitLab CI/CD
  • 云原生工具配置Kubernetes manifests
# Kubernetes Deployment示例
apiVersion: apps/v1
kind: Deployment
metadata:
    name: nginx-deployment
spec:
    replicas: 3
  1. 数据序列化
  • 替代JSON/XML传输复杂数据
  • API请求/响应的结构化数据描述
  1. 数据交换
  • 不同系统间传递结构化信息(如微服务配置同步)

如何创建和编辑YAML文件

  1. 文本编辑器
  • VS Code推荐安装YAML插件实现语法高亮和校验
  • Sublime Text / Vim等
  1. 专用工具
  • Online YAML Validator:校验语法有效性
  • yq命令行工具类似jq用于处理YAML

注意事项

  1. 缩进敏感
  • 必须使用空格通常2或4空格禁止使用Tab
  • 缩进错误会导致解析失败
  1. 键值对格式
  • 冒号后需有空格:key: value(而非key:value
  1. 特殊字符处理
  • 字符串包含:#等符号时建议使用引号:
message: "Hello:World"
comment: "This is a # symbol"
  1. 多行文本
  • 保留换行符:|
description: |
  This is a
  multi-line
  text.  
  • 折叠换行符:>
summary: >
  This will fold
  into a single line.  
  1. 数据类型标记
  • 强制指定类型:
boolean: !!bool "true"
timestamp: !!timestamp 2023-07-20T15:30:00Z

常见错误示例

# 错误缩进混用空格和Tab
user:
    name: Bob
  age: 25  # 缩进不一致

# 错误:键值对缺少空格
key1:value1  # 应改为 key1: value1

# 错误:未转义特殊字符
message: Line 1
        Line 2  # 缺少多行文本标识符

C语言版YAML库

varch提供的YAML库简便易用能完成大部分对于YAML文件的基础操作包含对yaml的加载和保存增删改查等操作。

接口

创建和删除yaml对象

yaml_t yaml_create(void);
void yaml_delete(yaml_t yaml);

其中yaml_t为yaml的结构体创建方法会生成一个空yaml对象。删除方法则删除指定的yaml对象。

yaml对象加载

yaml_t yaml_loads(const char* text, int flag);
yaml_t yaml_file_load(char* filename, int flag);

yaml对象可以从字符串文本中加载也可以从文件中加载。加载成功则会返回一个yaml对象失败则返回NULL。 flag为操作函数的一些flag定义如下

#define YAML_F_NONE                         (0)
#define YAML_F_DFLOW                        (0x01) /* dumps flow format */
#define YAML_F_LDOCS                        (0x02) /* load muti documents */
#define YAML_F_NOKEY                        (0x04) /* operate without key */
#define YAML_F_COMPLEX                      (0x08) /* operate with complex key */
#define YAML_F_ANCHOR                       (0x10) /* operate with anchor */
#define YAML_F_RECURSE                      (0x20) /* operate recurse */
#define YAML_F_REFERENCE                    (0x40) /* operate with reference */

当yaml对象加载失败的时候可以调用int yaml_error_info(int* line, int* column);函数进行定位错误。 错误类型包含

#define YAML_E_OK                           (0) /* ok, no error */
#define YAML_E_INVALID                      (1) /* invalid, not a valid expected value */
#define YAML_E_END                          (2) /* many invalid characters appear at the end */
#define YAML_E_KEY                          (3) /* parsing key, invalid key content found */
#define YAML_E_VALUE                        (4) /* parsing value, invalid value content found */
#define YAML_E_MEMORY                       (5) /* memory allocation failed */
#define YAML_E_SQUARE                       (6) /* mising ']' */
#define YAML_E_CURLY                        (7) /* mising '}' */
#define YAML_E_TAB                          (8) /* incorrect indent depth */
#define YAML_E_MIX                          (9) /* mix type */
#define YAML_E_FLINE                        (10) /* the first line of value can only be a literal */
#define YAML_E_LNUMBER                      (11) /* the number exceeds the storage capacity */
#define YAML_E_LBREAK                       (12) /* line break */
#define YAML_E_NANCHOR                      (13) /* null anchor */
#define YAML_E_IANCHOR                      (14) /* invalid anchor */
#define YAML_E_RANCHOR                      (15) /* repeat anchor */
#define YAML_E_UANCHOR                      (16) /* undefine anchor */
#define YAML_E_TANCHOR                      (17) /* type error anchor */
#define YAML_E_DATE                         (18) /* date error */
#define YAML_E_TARTGET                      (19) /* date error */

yaml对象转储

char* yaml_dumps(yaml_t yaml, int preset, int* len, int flag);
int yaml_file_dump(yaml_t yaml, char* filename);

首先yaml_dumps方法将yaml对象按格式转储为字符串。*len则是转换出来的字符串长度,传入NULL时候就是不获取长度。返回值则是转换出来的字符串,这个字符串是函数分配的,在结束使用需要free
yaml_file_dump方法则是在yaml_dumps的基础上将yaml转储到文件当中filename传入文件名,返回值为转储的长度,负值表示转储失败。

yaml添加子对象

#define yaml_seq_add_null(yaml)                     
#define yaml_seq_add_int(yaml, num)                 
#define yaml_seq_add_float(yaml, num)               
#define yaml_seq_add_string(yaml, string)           
#define yaml_seq_add_sequence(yaml, sequence)       
#define yaml_seq_add_mapping(yaml, mapping)         
#define yaml_map_add_null(yaml, key)                
#define yaml_map_add_int(yaml, key, num)            
#define yaml_map_add_float(yaml, key, num)          
#define yaml_map_add_string(yaml, key, string)      
#define yaml_map_add_sequence(yaml, key, sequence)  
#define yaml_map_add_mapping(yaml, key, mapping)    

以上方法分别在序列中和在映射中添加标量,其实就是套用 insert 类方法实现的

yaml_t yaml_insert_null(yaml_t yaml, const char* key, unsigned int index);
yaml_t yaml_insert_bool(yaml_t yaml, const char* key, unsigned int index, int b);
yaml_t yaml_insert_int(yaml_t yaml, const char* key, unsigned int index, int num);
yaml_t yaml_insert_float(yaml_t yaml, const char* key, unsigned int index, double num);
yaml_t yaml_insert_string(yaml_t yaml, const char* key, unsigned int index, const char* string);
yaml_t yaml_insert_sequence(yaml_t yaml, const char* key, unsigned int index, yaml_t sequence);
yaml_t yaml_insert_mapping(yaml_t yaml, const char* key, unsigned int index, yaml_t mapping);
yaml_t yaml_insert_document(yaml_t yaml, unsigned int index, yaml_t document);
yaml_t yaml_insert_reference(yaml_t yaml, const char* key, unsigned int index, const char* anchor, yaml_t doc);

这些insert类方法用于在yaml对象的指定位置插入不同类型的子对象。key为插入对象的键,index为插入的位置。返回值为插入操作后的yaml对象,若插入失败则返回NULL。具体如下:

  • yaml_insert_null:插入一个空类型的子对象。
  • yaml_insert_bool:插入一个布尔类型的子对象,b为布尔值(YAML_FALSEYAML_TRUE)。
  • yaml_insert_int:插入一个整数类型的子对象,num为整数值。
  • yaml_insert_float:插入一个浮点数类型的子对象,num为浮点数值。
  • yaml_insert_string:插入一个字符串类型的子对象,string为字符串值。
  • yaml_insert_sequence:插入一个序列类型的子对象,sequence为序列对象。
  • yaml_insert_mapping:插入一个映射类型的子对象,mapping为映射对象。
  • yaml_insert_document:插入一个文档类型的子对象,document为文档对象。
  • yaml_insert_reference:插入一个引用类型的子对象,anchor为引用的锚点,doc为引用的文档对象。

yaml移除子对象

int yaml_remove(yaml_t yaml, const char* key, unsigned int index);

移除yaml对象中特定的键为key的第index个子对象。若移除成功,返回YAML_E_OK;若移除失败,返回相应的错误码。

yaml对象属性操作

int yaml_type(yaml_t yaml);
unsigned int yaml_size(yaml_t yaml);
  • yaml_type:获取yaml对象的类型,返回值为YAML_TYPE_*系列宏定义的值,用于判断对象是NULL、布尔、整数、浮点数、字符串、序列、映射、文档、引用或复杂键等类型。
  • yaml_size:获取yaml对象的大小。对于序列或映射类型的对象,返回其元素的数量;对于其他类型的对象,返回值的含义可能因具体实现而异。

yaml对象比较和复制

int yaml_compare(yaml_t yaml, yaml_t cmp, int flag);
yaml_t yaml_copy(yaml_t yaml, int flag);
  • yaml_compare:比较两个yaml对象。flag为比较的标志位,用于指定比较的方式。返回值为比较结果,具体含义由实现决定,通常 0 表示相等,非 0 表示不相等。
  • yaml_copy:复制一个yaml对象。flag为复制的标志位,用于指定复制的方式。返回值为复制后的yaml对象,若复制失败则返回NULL

yaml对象键操作

yaml_t yaml_set_key(yaml_t yaml, const char* key);
yaml_t yaml_set_key_complex(yaml_t yaml, yaml_t key);
const char* yaml_key(yaml_t yaml);
yaml_t yaml_key_complex(yaml_t yaml);
  • yaml_set_key:为yaml对象设置一个简单键,key为键的字符串值。返回值为设置键后的yaml对象。
  • yaml_set_key_complex:为yaml对象设置一个复杂键,key为复杂键的yaml对象。返回值为设置键后的yaml对象。
  • yaml_key:获取yaml对象的简单键,返回值为键的字符串值。
  • yaml_key_complex:获取yaml对象的复杂键,返回值为复杂键的yaml对象。

yaml对象值设置

yaml_t yaml_set_null(yaml_t yaml);
yaml_t yaml_set_bool(yaml_t yaml, int b);
yaml_t yaml_set_int(yaml_t yaml, int num);
yaml_t yaml_set_float(yaml_t yaml, double num);
yaml_t yaml_set_string(yaml_t yaml, const char* string);
yaml_t yaml_set_date(yaml_t yaml, int year, char month, char day);
yaml_t yaml_set_time(yaml_t yaml, char hour, char minute, char second, int msec);
yaml_t yaml_set_utc(yaml_t yaml, char hour, char minute);
yaml_t yaml_set_sequence(yaml_t yaml, yaml_t sequence);
yaml_t yaml_set_mapping(yaml_t yaml, yaml_t mapping);
yaml_t yaml_set_document(yaml_t yaml, yaml_t document);

这些方法用于设置yaml对象的值,返回值为设置值后的yaml对象。具体如下:

  • yaml_set_null:将yaml对象的值设置为空类型。
  • yaml_set_bool:将yaml对象的值设置为布尔类型,b为布尔值(YAML_FALSEYAML_TRUE)。
  • yaml_set_int:将yaml对象的值设置为整数类型,num为整数值。
  • yaml_set_float:将yaml对象的值设置为浮点数类型,num为浮点数值。
  • yaml_set_string:将yaml对象的值设置为字符串类型,string为字符串值。
  • yaml_set_date:将yaml对象的值设置为日期类型,year为年份,month为月份,day为日期。
  • yaml_set_time:将yaml对象的值设置为时间类型,hour为小时,minute为分钟,second为秒,msec为毫秒。
  • yaml_set_utc:将yaml对象的值设置为 UTC 时间偏移类型,hour为小时偏移,minute为分钟偏移。
  • yaml_set_sequence:将yaml对象的值设置为序列类型,sequence为序列对象。
  • yaml_set_mapping:将yaml对象的值设置为映射类型,mapping为映射对象。
  • yaml_set_document:将yaml对象的值设置为文档类型,document为文档对象。

yaml对象值获取

int yaml_value_bool(yaml_t yaml);
int yaml_value_int(yaml_t yaml);
double yaml_value_float(yaml_t yaml);
const char* yaml_value_string(yaml_t yaml);
yaml_t yaml_value_sequence(yaml_t yaml);
yaml_t yaml_value_mapping(yaml_t yaml);
yaml_t yaml_value_document(yaml_t yaml);

这些方法用于获取yaml对象的值,返回值为获取到的值。具体如下:

  • yaml_value_bool:获取yaml对象的布尔值。
  • yaml_value_int:获取yaml对象的整数值。
  • yaml_value_float:获取yaml对象的浮点数值。
  • yaml_value_string:获取yaml对象的字符串值。
  • yaml_value_sequence:获取yaml对象的序列值,返回序列对象。
  • yaml_value_mapping:获取yaml对象的映射值,返回映射对象。
  • yaml_value_document:获取yaml对象的文档值,返回文档对象。

yaml对象子元素操作

yaml_t yaml_attach(yaml_t yaml, unsigned int index, yaml_t attach);
yaml_t yaml_dettach(yaml_t yaml, unsigned int index);
  • yaml_attach:在yaml对象的指定位置index附加一个yaml子对象attach。返回值为操作后的yaml对象,若操作失败则返回NULL
  • yaml_dettach:从yaml对象的指定位置index分离一个子对象。返回值为分离出的子对象,若操作失败则返回NULL

yaml对象索引和子对象获取

unsigned int yaml_get_index(yaml_t yaml, const char* key, unsigned int index);
unsigned int yaml_get_index_complex(yaml_t yaml, yaml_t key);
yaml_t yaml_get_child(yaml_t yaml, const char* key, unsigned int index);
yaml_t yaml_get_child_complex(yaml_t yaml, yaml_t key);
  • yaml_get_index:获取yaml对象中键为key的第index个子对象的索引。返回值为子对象的索引,若未找到则返回YAML_INV_INDEX
  • yaml_get_index_complex:获取yaml对象中复杂键为key的子对象的索引。返回值为子对象的索引,若未找到则返回YAML_INV_INDEX
  • yaml_get_child:获取yaml对象中键为key的第index个子对象。返回值为子对象的yaml对象,若未找到则返回NULL
  • yaml_get_child_complex:获取yaml对象中复杂键为key的子对象。返回值为子对象的yaml对象,若未找到则返回NULL

yaml对象锚点和别名操作

const char* yaml_get_alias(yaml_t yaml);
yaml_t yaml_get_anchor(yaml_t yaml, unsigned int index);
yaml_t yaml_set_alias(yaml_t yaml, const char* alias, yaml_t doc);
yaml_t yaml_set_anchor(yaml_t yaml, const char* anchor, yaml_t doc);
unsigned int yaml_anchor_size(yaml_t yaml);
  • yaml_get_alias:获取yaml对象的别名,返回值为别名的字符串值。
  • yaml_get_anchor:获取yaml对象中指定索引index的锚点对象,返回值为锚点的yaml对象。
  • yaml_set_alias:为yaml对象设置别名,alias为别名的字符串值,doc为关联的文档对象。返回值为设置别名后的yaml对象。
  • yaml_set_anchor:为yaml对象设置锚点,anchor为锚点的字符串值,doc为关联的文档对象。返回值为设置锚点后的yaml对象。
  • yaml_anchor_size:获取yaml对象中锚点的数量。返回值为锚点的数量。

参考例子

生成yaml文件

static void test_dump(void)
{
    yaml_t root, node, temp;

    root = yaml_create();
    yaml_set_mapping(root, NULL);
    
    node = yaml_map_add_mapping(root, "mapping", NULL);
    yaml_map_add_string(node, "version", "1.0.0");
    yaml_map_add_string(node, "author", "Lamdonn");
    yaml_map_add_string(node, "license", "GPL-2.0");

    node = yaml_map_add_sequence(root, "sequence", NULL);
    yaml_seq_add_string(node, "file description");
    yaml_seq_add_string(node, "This is a C language version of yaml streamlined parser");
    yaml_seq_add_string(node, "Copyright (C) 2023 Lamdonn.");
    temp = yaml_seq_add_mapping(node, NULL);
    yaml_map_add_string(temp, "age", "18");
    yaml_map_add_string(temp, "height", "178cm");
    yaml_map_add_string(temp, "weight", "75kg");

    yaml_remove(temp, 0, 1);

    /* preview yaml */
    yaml_preview(root);

    /* dump yaml file */
    yaml_file_dump(root, WRITE_FILE);

    yaml_delete(root);
}

转储的文件 write.yaml

mapping: 
  version: 1.0.0
  author: Lamdonn
  license: GPL-2.0
sequence: 
  - file description
  - This is a C language version of yaml streamlined parser
  - Copyright (C) 2023 Lamdonn.
  - 
    age: 18
    weight: 75kg

加载yaml文件

同样加载yaml文件

static void test_load(void)
{
    yaml_t root = NULL, x = NULL;
    
    root = yaml_file_load(READ_FILE, YAML_F_LDOCS);
    if (!root)
    {
        int type = 0, line = 0, column = 0;
        type = yaml_error_info(&line, &column);
        printf("error at line %d column %d type %d.\r\n", line, column, type);
        return;
    }
    printf("load success!\r\n");

    yaml_preview(root);

    yaml_delete(root);
}

运行结果:

load success!
mapping:
  version: 1.0.0
  author: Lamdonn
  license: GPL-2.0
sequence:
  - file description
  - This is a C language version of yaml streamlined parser
  - Copyright (C) 2023 Lamdonn.
  -
    age: 18
    weight: 75kg