searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

使用libcyaml实现YAML解析和序列化

2023-11-27 09:33:25
210
0

前言

 YAML(Yet Another Markup Language)是一种人类可读的数据序列化格式,常用于(如K8S及Ansible)配置文件和数据交换。在C语言中,我们可以使用libyaml来解析和处理YAML格式的数据, libcyaml 是基于libyaml 封装的基于schema的YAML解析和序列化工具, 基于ISC协议开源,用户能够方便集成到业务代码。本文将介绍如何使用libcayml实现解析YAML到结构体。

前置准备

1.    安装依赖包
libcayml 基于libyaml作为底层的YAML读写库,因此需要安装libyaml及其开发库,否则在编译时将出现错误。

$ yum install -y libyaml libyaml-devel

2.    下载代码

git clone github.com/tlsa/libcyaml.git lib
cd lib
git clone v1.4.1 -b v1.4.1

项目集成

1.    打开调试开关
在项目中,通常需要打开调试便于后期问题定位,因此修改Makefile打开编译调试开关

diff --git a/Makefile b/Makefile
index 77f2fb3..01c7355 100644
--- a/Makefile
+++ b/Makefile
@@ -18,7 +18,7 @@ VERSION_DEVEL = 0
 VERSION_STR = $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH)

 # Default variant depends on whether it's a development build.
-ifeq ($(VERSION_DEVEL), 1)
+ifneq ($(ENVTYPE),release)
        VARIANT = debug
 else
        VARIANT = release
@@ -80,7 +80,7 @@ else ifeq ($(VARIANT), san)
        CFLAGS += -O0 -g -fsanitize=address -fsanitize=undefined -fno-sanitize-recover
        LDFLAGS += -fsanitize=address -fsanitize=undefined -fno-sanitize-recover
 else
-       CFLAGS += -O2 -DNDEBUG
+       CFLAGS += -g -O2 -DNDEBUG
 endif

 ifneq ($(filter coverage,$(MAKECMDGOALS)),)
--
2.27.0

2.    代码开发
参考代码仓库中examples/planner/main.c 进行代码开发:
定义结构体

struct task {
    const char *name;
    enum task_flags flags;
    struct duration estimate;

    const char **depends;
    unsigned depends_count;

    const char **people;
    unsigned n_people;
};

定义结构体对应的schema:

static const cyaml_schema_field_t task_fields_schema[] = {
    CYAML_FIELD_STRING_PTR(
            "name", CYAML_FLAG_POINTER,
            struct task, name, 0, CYAML_UNLIMITED),
    CYAML_FIELD_FLAGS(
            "flags", CYAML_FLAG_OPTIONAL | CYAML_FLAG_STRICT,
            struct task, flags, task_flags_strings,
            CYAML_ARRAY_LEN(task_flags_strings)),
    CYAML_FIELD_MAPPING(
            "estimate", CYAML_FLAG_DEFAULT,
            struct task, estimate, duration_fields_schema),
    CYAML_FIELD_SEQUENCE(
            "depends", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL,
            struct task, depends,
            &string_ptr_schema, 0, CYAML_UNLIMITED),
    CYAML_FIELD_SEQUENCE_COUNT(
            "people", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL,
            struct task, people, n_people,
            &string_ptr_schema, 0, CYAML_UNLIMITED),
    CYAML_FIELD_END
};

static const cyaml_schema_value_t task_schema = {
    CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT,
            struct task, task_fields_schema),
};


如果结构体作为另外一个结构体的成员, 可以参考plan的相关定义:

struct plan {
    …
    struct task *tasks;
    uint64_t tasks_count;
};

 
Schema定义:

static const cyaml_schema_field_t plan_fields_schema[] = {
   …
   CYAML_FIELD_SEQUENCE(
            "tasks", CYAML_FLAG_POINTER,
            struct plan, tasks,
            &task_schema, 0, CYAML_UNLIMITED),
   …
};


YAML 配置

static const cyaml_config_t config = {
    .log_fn = cyaml_log,
    .mem_fn = cyaml_mem,
    .log_level = CYAML_LOG_INFO,
};

这里使用默认的日志及内存分配函数,如有需要可实现自定义的方法。
解析yaml文件到结构体

    err = cyaml_load_file(argv[ARG_PATH_IN], &config,
            &plan_schema, (void **) &plan, NULL);

序列化结构体并写到文件:

   err = cyaml_save_file(argv[ARG_PATH_OUT], &config,
            &plan_schema, plan, 0);

最后,别记得释放内存:

    cyaml_free(&config, &plan_schema, plan, 0);

编译libcayml到源码

CFLAGS += -I lib/include

LIBS = -lyaml -Llib -lcyaml

OBJS = main.o

all: libs $(TARGET)

libs:
    make ENVTYPE=$(ENVTYPE) -C lib
    install -m 0644 lib/build/$(VARIANT)/libcyaml.a lib/

$(TARGET): $(OBJS)
    -mkdir -p ./build/
    gcc $(CFLAGS) -o $@ $^ $(LIBS)

执行make命令即能完成代码编译。

总结


YAML是一种方便人类阅读和编写的数据序列化格式,在C语言中可以使用libyaml库来解析和处理YAML格式的数据。本文介绍了如何使用基于libyaml封装的开源libcyaml库,以及如何使用libcyaml库来加载、解析和处理YAML文件。

0条评论
0 / 1000
l****n
6文章数
0粉丝数
l****n
6 文章 | 0 粉丝
原创

使用libcyaml实现YAML解析和序列化

2023-11-27 09:33:25
210
0

前言

 YAML(Yet Another Markup Language)是一种人类可读的数据序列化格式,常用于(如K8S及Ansible)配置文件和数据交换。在C语言中,我们可以使用libyaml来解析和处理YAML格式的数据, libcyaml 是基于libyaml 封装的基于schema的YAML解析和序列化工具, 基于ISC协议开源,用户能够方便集成到业务代码。本文将介绍如何使用libcayml实现解析YAML到结构体。

前置准备

1.    安装依赖包
libcayml 基于libyaml作为底层的YAML读写库,因此需要安装libyaml及其开发库,否则在编译时将出现错误。

$ yum install -y libyaml libyaml-devel

2.    下载代码

git clone github.com/tlsa/libcyaml.git lib
cd lib
git clone v1.4.1 -b v1.4.1

项目集成

1.    打开调试开关
在项目中,通常需要打开调试便于后期问题定位,因此修改Makefile打开编译调试开关

diff --git a/Makefile b/Makefile
index 77f2fb3..01c7355 100644
--- a/Makefile
+++ b/Makefile
@@ -18,7 +18,7 @@ VERSION_DEVEL = 0
 VERSION_STR = $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH)

 # Default variant depends on whether it's a development build.
-ifeq ($(VERSION_DEVEL), 1)
+ifneq ($(ENVTYPE),release)
        VARIANT = debug
 else
        VARIANT = release
@@ -80,7 +80,7 @@ else ifeq ($(VARIANT), san)
        CFLAGS += -O0 -g -fsanitize=address -fsanitize=undefined -fno-sanitize-recover
        LDFLAGS += -fsanitize=address -fsanitize=undefined -fno-sanitize-recover
 else
-       CFLAGS += -O2 -DNDEBUG
+       CFLAGS += -g -O2 -DNDEBUG
 endif

 ifneq ($(filter coverage,$(MAKECMDGOALS)),)
--
2.27.0

2.    代码开发
参考代码仓库中examples/planner/main.c 进行代码开发:
定义结构体

struct task {
    const char *name;
    enum task_flags flags;
    struct duration estimate;

    const char **depends;
    unsigned depends_count;

    const char **people;
    unsigned n_people;
};

定义结构体对应的schema:

static const cyaml_schema_field_t task_fields_schema[] = {
    CYAML_FIELD_STRING_PTR(
            "name", CYAML_FLAG_POINTER,
            struct task, name, 0, CYAML_UNLIMITED),
    CYAML_FIELD_FLAGS(
            "flags", CYAML_FLAG_OPTIONAL | CYAML_FLAG_STRICT,
            struct task, flags, task_flags_strings,
            CYAML_ARRAY_LEN(task_flags_strings)),
    CYAML_FIELD_MAPPING(
            "estimate", CYAML_FLAG_DEFAULT,
            struct task, estimate, duration_fields_schema),
    CYAML_FIELD_SEQUENCE(
            "depends", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL,
            struct task, depends,
            &string_ptr_schema, 0, CYAML_UNLIMITED),
    CYAML_FIELD_SEQUENCE_COUNT(
            "people", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL,
            struct task, people, n_people,
            &string_ptr_schema, 0, CYAML_UNLIMITED),
    CYAML_FIELD_END
};

static const cyaml_schema_value_t task_schema = {
    CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT,
            struct task, task_fields_schema),
};


如果结构体作为另外一个结构体的成员, 可以参考plan的相关定义:

struct plan {
    …
    struct task *tasks;
    uint64_t tasks_count;
};

 
Schema定义:

static const cyaml_schema_field_t plan_fields_schema[] = {
   …
   CYAML_FIELD_SEQUENCE(
            "tasks", CYAML_FLAG_POINTER,
            struct plan, tasks,
            &task_schema, 0, CYAML_UNLIMITED),
   …
};


YAML 配置

static const cyaml_config_t config = {
    .log_fn = cyaml_log,
    .mem_fn = cyaml_mem,
    .log_level = CYAML_LOG_INFO,
};

这里使用默认的日志及内存分配函数,如有需要可实现自定义的方法。
解析yaml文件到结构体

    err = cyaml_load_file(argv[ARG_PATH_IN], &config,
            &plan_schema, (void **) &plan, NULL);

序列化结构体并写到文件:

   err = cyaml_save_file(argv[ARG_PATH_OUT], &config,
            &plan_schema, plan, 0);

最后,别记得释放内存:

    cyaml_free(&config, &plan_schema, plan, 0);

编译libcayml到源码

CFLAGS += -I lib/include

LIBS = -lyaml -Llib -lcyaml

OBJS = main.o

all: libs $(TARGET)

libs:
    make ENVTYPE=$(ENVTYPE) -C lib
    install -m 0644 lib/build/$(VARIANT)/libcyaml.a lib/

$(TARGET): $(OBJS)
    -mkdir -p ./build/
    gcc $(CFLAGS) -o $@ $^ $(LIBS)

执行make命令即能完成代码编译。

总结


YAML是一种方便人类阅读和编写的数据序列化格式,在C语言中可以使用libyaml库来解析和处理YAML格式的数据。本文介绍了如何使用基于libyaml封装的开源libcyaml库,以及如何使用libcyaml库来加载、解析和处理YAML文件。

文章来自个人专栏
开发工具集
4 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0