go语言yaml配置 go解析yaml文件

Golang项目中引入yaml.v2配置文件

在Go语言项目中,常用的配置文件yaml、toml、json、xml、ini几种,因为本章主要讲解yaml配置文件的使用方法,其他几种配置文件在这里就不展开了介绍了,大家有兴趣可以自行百度。

创新互联长期为数千家客户提供的网站建设服务,团队从业经验10年,关注不同地域、不同群体,并针对不同对象提供差异化的产品和服务;打造开放共赢平台,与合作伙伴共同营造健康的互联网生态环境。为任丘企业提供专业的成都做网站、成都网站设计,任丘网站改版等技术服务。拥有10余年丰富建站经验和众多成功案例,为您定制开发。

yaml文件的语法网上有很多的教程,大家自行百度,这里也推荐两个链接:

yaml文件解析使用的是github上第三方开源框架 gopkg.in/yaml.v2 ,下面详细介绍安装和使用的方法:

参考链接:

34.Go YAML

YAML是一种流行的格式,用于以人类友好的格式序列化数据, 类似JSON但更易于阅读。

由于其表达能力和可读性,YAML作为配置文件的格式很受欢迎。

它也用于更复杂的场景中,例如推动Ansible服务器自动化。

标准库中没有用于处理YAML格式的软件包,但是社区库包括gopkg.in/yaml.v2

将YAML文件读取到Go结构中:

YAML:

YAML解码与JSON解码非常相似。

如果你知道YAML文件的结构,则可以定义映射该结构的结构,并将指向顶级结构的结构的指针传递给yaml.Decoder.Decode()函数(或从[]进行解码的yaml.Unmarshal())。 字节片)。

YAML解码器在结构字段名称和YAML文件中的名称之间进行智能映射,以便 YAML中的名称值被解码为结构中的字段名称。

最好使用yaml struct标签创建显式映射。 我仅在示例中省略了它们,以说明未指定它们时的行为。

将struct写入YAML文件:

yaml.Marshal将interface {}作为参数。 可以传递任何Go值,并将其类型包装到interface {}中。

Marshaller将使用反射检查传递的值并将其编码为YAML字符串。

在序列化结构时,仅对导出的字段(其名称以大写字母开头)进行序列化/反序列化。

在我们的示例中,未对fullName进行序列化。

结构被序列化为YAML字典。 默认情况下,字典键与结构字段名称相同。

结构字段名称在字典键名称下序列化。

我们可以提供带有struct标签的自定义映射。

我们可以将任意的struct标签字符串附加到struct字段。

yaml:“ age”指示YAML编码器/解码器将名称age用于表示字段Age的字典关键字。

序列化结构时,将值和指针传递给它会产生相同的结果。

传递指针效率更高,因为按值传递会创建不必要的副本。

YAML中多行字符串的配置方法

有时候我们会在配置文件中配置一段文字说明,这种时候通常会出现两种需求:

简单的说,就是:

假设,我们需要配置这样一段文字:

下面,就针对上面的两种情况来看看可以怎么来实现:

这个需求下,我们希望配置和显示都按句子换行,就是这样:

这样写:

最终输出:

通过 \n 在显示的时候换行,通过配置行末的 \ 让这个字符串换行继续写(这个必须有,如果没有第二行行首会多一个空格)。

注意 :这里必须使用双引号来定义字符串,不能用单引号。因为单引号是不支持 \n 换行的。

在方法一种,其实我们在文字中加入了几个转义符号,其实对于阅读并不方便。在方法二中,将介绍更适合阅读的几种形式:

如上面一共有三种配置都会自动按配置中所写的换行来换行,但是在文末会有一些区别,有的会增加一个空行,有的不会,有的会新增两个空行,具体说明如下:

这个需求下,我们希望配置里是按行写的,但是显示是如下面这样在一行的:

最粗暴的写法,反正不用换行,那就直接写了:

这里不论用双引号还是单引号都是可以的。因为不存在需要转移的内容,所以总体还算清晰。

比较好的表述方式就是使用 、 + 、 - 来定义,比如下面这几种:

这三种都不会对配置中的换行进行实际换行,但是依然在文末的处理会有一些小区别,具体如下:

C语言操作yaml配置文件通用操作工具

在go语言中使用viper之类的库很方便的处理yaml配置文件,但是在c语言中就比较麻烦,经过一番思索和借助强大的github,发现了一个libyaml c库,但是网上的例子都比较麻烦,而且比较繁琐,就想法作了一个相对比较容易配置的解析应用,可以简单地类似viper 的模式进行配置实现不同的配置文件读取。如你的配置文件很复杂请按格式修改KeyValue 全局变量,欢迎大家一起完善

库请自行下载 GitHub - yaml/libyaml: Canonical source repository for LibYAML

直接上代码

yaml示例文件

%YAML 1.1

---

mqtt:

subtopic: "Control/#"

pubtopic: "bbt"

qos: 1

serveraddress: "tcp://192.168.0.25:1883"

clientid: "kvm_test"

writelog: false

writetodisk: false

outputfile: "./receivedMessages.txt"

hearttime: 30

#ifndef __CONFIG_H__

#define __CONFIG_H__

#ifdef __cplusplus

extern "C" {

#endif

/************************/

/* Minimum YAML version */

/************************/

#define YAML_VERSION_MAJOR 1

#define YAML_VERSION_MINOR 1

#define STRUCT_TYPE_NAME 100

#define INT_TYPE_NAME 101

#define STRING_TYPE_NAME 102

#define BOOL_TYPE_NAME 103

#define FLOAT_TYPE_NAME 104

#define MAP_TYPE_NAME 105

#define LIST_TYPE_NAME 106

typedef struct{

char *key;

void *value;

int valuetype;

char *parent;

}KeyValue,*pKeyValue;

#ifdef __cplusplus

}

#endif

#endif

#include

#include

#include

#include

#include

#include

#include

#include "config.h"

typedef struct {

char *SUBTOPIC; //string `yaml:"subtopic" mapstructure:"subtopic"` //"topic1"

char *PUBTOPIC; //string `yaml:"pubtopic" mapstructure:"pubtopic"`

int QOS; //byte `yaml:"qos" mapstructure:"qos"` //1

char *SERVERADDRESS; //string `yaml:"serveraddress" mapstructure:"serveraddress"` //= "tcp://mosquitto:1883"

char *CLIENTID; //string `yaml:"clientid" mapstructure:"clientid"` //= "mqtt_subscriber"

int HEARTTIME; //int `yaml:"hearttime" mapstructure:"hearttime"`

// CommandLocalPath string `yam:"commanlocalpath"`

}mqttSection,*pmqttSection;

typedef struct {

mqttSection Mqtt;// `yaml:"mqtt" mapstructure:"mqtt"`

// KVM kvmSection `yaml:"kvm" mapstructure:"kvm"`

}ConfigT;

ConfigT config;

static KeyValue webrtcconfig[]={

{"mqtt",config,STRUCT_TYPE_NAME,NULL},

{"subtopic",(config.Mqtt.SUBTOPIC),STRING_TYPE_NAME,"mqtt"},

{"pubtopic",(config.Mqtt.PUBTOPIC),STRING_TYPE_NAME,"mqtt"},

{"qos",(config.Mqtt.QOS),INT_TYPE_NAME,"mqtt"},

{"serveraddress",(config.Mqtt.SERVERADDRESS),STRING_TYPE_NAME,"mqtt"},

{"clientid",(config.Mqtt.CLIENTID),STRING_TYPE_NAME,"mqtt"},

{"hearttime",(config.Mqtt.HEARTTIME),INT_TYPE_NAME,"mqtt"},

{NULL,NULL,0,NULL},

};

int printConfig(ConfigT * pconfig){

if(pconfig==NULL) return -1;

printf("mqtt:r ");

if(pconfig-Mqtt.SUBTOPIC!=NULL) {printf("subtopic: %sr ",pconfig-Mqtt.SUBTOPIC); }

if(pconfig-Mqtt.SUBTOPIC!=NULL) {printf("pubtopic: %sr ",pconfig-Mqtt.PUBTOPIC); }

printf("qos: %dr ",config.Mqtt.QOS);

if(pconfig-Mqtt.SERVERADDRESS!=NULL) {printf("serveraddress: %sr ",pconfig-Mqtt.SERVERADDRESS); }

if(pconfig-Mqtt.CLIENTID!=NULL) {printf("clientid: %sr ",pconfig-Mqtt.CLIENTID); }

printf("hearttime: %dr ",config.Mqtt.HEARTTIME);

}

int freeConfig(ConfigT * pconfig){

if(pconfig==NULL) return -1;

if(pconfig-Mqtt.SERVERADDRESS!=NULL) {free(pconfig-Mqtt.SERVERADDRESS); }

if(pconfig-Mqtt.CLIENTID!=NULL) {free(pconfig-Mqtt.CLIENTID); }

if(pconfig-Mqtt.SUBTOPIC!=NULL) {free(pconfig-Mqtt.SUBTOPIC); }

}

char currentkey[100];

void getvalue(yaml_event_t event,pKeyValue *ppconfigs){

char *value = (char *)event.data.scalar.value;

pKeyValue pconfig=*ppconfigs;

char *pstringname;

while(pconfig-key!=NULL){

if(currentkey[0]!=0){

if(!strcmp(currentkey,pconfig-key))

{

switch(pconfig-valuetype){

case STRING_TYPE_NAME:

pstringname=strdup(value);

printf("get string value %sr ",pstringname);

*((char**)pconfig-value)=pstringname;

memset(currentkey, 0, sizeof(currentkey));

break;

case INT_TYPE_NAME:

*((int*)(pconfig-value))=atoi(value);

memset(currentkey, 0, sizeof(currentkey));

break;

case BOOL_TYPE_NAME:

if(!strcmp(value,"true")) *((bool*)(pconfig-value))=true;

else *((bool*)(pconfig-value))=false;

memset(currentkey, 0, sizeof(currentkey));

break;

case FLOAT_TYPE_NAME:

*((float*)(pconfig-value))=atof(value);

memset(currentkey, 0, sizeof(currentkey));

break;

case STRUCT_TYPE_NAME:

case MAP_TYPE_NAME:

case LIST_TYPE_NAME:

memset(currentkey, 0, sizeof(currentkey));

strncpy(currentkey,value,strlen(value));

break;

default:

break;

}

break;

}

//continue;

}else{

if(!strcmp(value,pconfig-key)){

strncpy(currentkey,pconfig-key,strlen(pconfig-key));

break;

}

}

pconfig++;

}

}

int Load_YAML_Config( char *yaml_file, KeyValue *(configs[]) )

{

struct stat filecheck;

yaml_parser_t parser;

yaml_event_t event;

bool done = 0;

unsigned char type = 0;

unsigned char sub_type = 0;

if (stat(yaml_file, filecheck) != false )

{

printf("[%s, line %d] Cannot open configuration file '%s'! %s", __FILE__, __LINE__, yaml_file, strerror(errno) );

return -1;

}

FILE *fh = fopen(yaml_file, "r");

if (!yaml_parser_initialize(parser))

{

printf("[%s, line %d] Failed to initialize the libyaml parser. Abort!", __FILE__, __LINE__);

return -1;

}

if (fh == NULL)

{

printf("[%s, line %d] Failed to open the configuration file '%s' Abort!", __FILE__, __LINE__, yaml_file);

return -1;

}

memset(currentkey, 0, sizeof(currentkey));

/* Set input file */

yaml_parser_set_input_file(parser, fh);

while(!done)

{

if (!yaml_parser_parse(parser, event))

{

/* Useful YAML vars: parser.context_mark.line+1, parser.context_mark.column+1, parser.problem, parser.problem_mark.line+1, parser.problem_mark.column+1 */

printf( "[%s, line %d] libyam parse error at line %ld in '%s'", __FILE__, __LINE__, parser.problem_mark.line+1, yaml_file);

}

if ( event.type == YAML_DOCUMENT_START_EVENT )

{

//yaml file first line is version

//%YAML 1.1

//---

yaml_version_directive_t *ver = event.data.document_start.version_directive;

if ( ver == NULL )

{

printf( "[%s, line %d] Invalid configuration file. Configuration must start with "%%YAML 1.1"", __FILE__, __LINE__);

}

int major = ver-major;

int minor = ver-minor;

if (! (major == YAML_VERSION_MAJOR minor == YAML_VERSION_MINOR) )

{

printf( "[%s, line %d] Configuration has a invalid YAML version. Must be 1.1 or above", __FILE__, __LINE__);

return -1;

}

}

else if ( event.type == YAML_STREAM_END_EVENT )

{

done = true;

}

else if ( event.type == YAML_MAPPING_END_EVENT )

{

sub_type = 0;

}

else if ( event.type == YAML_SCALAR_EVENT )

{

getvalue(event,configs);

}

}

return 0;

}

int main(int argc, char *argv[]){

pKeyValue pconfig=webrtcconfig[0];

Load_YAML_Config("../../etc/kvmagent.yml",pconfig);

printConfig(config);

freeConfig(config);

}

现代配置指南——YAML 比 JSON 高级在哪

一直以来,前端工程中的配置大多都是 .js 文件或者 .json 文件,最常见的比如:

这些配置对前端非常友好,因为都是我们熟悉的 JS 对象结构。一般静态化的配置会选择 json 文件,而动态化的配置,涉及到引入其他模块,因此会选择 js 文件。

还有现在许多新工具同时支持多种配置,比如 Eslint ,两种格式的配置任你选择:

后来不知道什么时候,突然出现了一种以 .yaml 或 .yml 为后缀的配置文件。一开始以为是某个程序的专有配置,后来发现这个后缀的文件出现的频率越来越高,甚至 Eslint 也支持了第三种格式的配置 .eslintrc.yml 。

既然遇到了,那就去 探索 它!

下面我们从 YAML 的 出现背景 , 使用场景 , 具体用法 , 高级操作 四个方面,看一下这个流行的现代化配置的神秘之处。

一个新工具的出现避免不了有两个原因:

YAML 这种新工具就属于后者。其实在 yaml 出现之前 js+json 用得也不错,也没什么特别难以处理的问题;但是 yaml 出现以后,开始觉得它好乱呀什么东西,后来了解它后,越用越喜欢,一个字就是优雅。

很多文章说选择 yaml 是因为 json 的各种问题,json 不适合做配置文件,这我觉得有些言过其实了。我更愿意将 yaml 看做是 json 的升级,因为 yaml 在格式简化和体验上表现确实不错,这个得承认。

下面我们对比 YAML 和 JSON,从两方面分析:

JSON 比较繁琐的地方是它严格的格式要求。比如这个对象:

在 JSON 中以下写法通通都是错的:

字符串的值必须 k-v 都是 "" 才行:

虽然是统一格式,但是使用上确实有不便利的地方。比如我在浏览器上测出了接口错误。然后把参数拷贝到 Postman 里调试,这时就我要手动给每个属性和值加 "" 号,非常繁琐。

YAML 则是另辟蹊径,直接把字符串符号干掉了。上面对象的同等 yaml 配置如下:

没错,就这么简单!

除了 "" 号,yaml 觉得 {} 和 [] 这种符号也是多余的,不如一起干掉。

于是呢,以这个对象数组为例:

转换成 yaml 是这样的:

对比一下这个精简程度,有什么理由不爱它?

说起增加的部分,最值得一提的,是 YAML 支持了 注释 。

用 JSON 写配置是不能有注释的,这就意味着我们的配置不会有备注,配置多了会非常凌乱,这是最不人性化的地方。

现在 yaml 支持了备注,以后配置可以是这样的:

把这种配置丢给新同事,还怕他看不懂配了啥吗?

除注释外,还支持配置复用的相关功能,这个后面说。

我接触的第一个 yaml 配置是 Flutter 项目的包管理文件 pubspec.yaml ,这个文件的作用和前端项目中的 package.json 一样,用于存放一些全局配置和应用依赖的包和版本。

看一下它的基本结构:

你看这个结构和 package.json 是不是基本一致? dependencies 下列出应用依赖和版本, dev_dependencies 下的则是开发依赖。

后来在做 CI/CD 自动化部署的时候,我们用到了 GitHub Action。它需要多个 yaml 文件来定义不同的工作流,这个配置可比 flutter 复杂得多。

其实不光 GitHub Action,其他流行的类似的构建工具如 GitLab CI/CD,circleci,全部都是齐刷刷的 yaml 配置,因此如果你的项目要做 CI/CD 持续集成,不懂 yaml 语法肯定是不行的。

还有,接触过 Docker 的同学肯定知道 Docker Compose,它是 Docker 官方的单机编排工具,其配置文件 docker-compose.yml 也是妥妥的 yaml 格式。现在 Docker 正是如日中天的时候,使用 Docker 必然免不了编排,因此 yaml 语法早晚也要攻克。

上面说的这 3 个案例,几乎都是现代最新最流行的框架/工具。从它们身上可以看出来,yaml 必然是下一代配置文件的标准,并且是 前端-后端-运维 的通用标准。

说了这么多,你跃跃欲试了吗?下面我们详细介绍 yaml 语法。

介绍 yaml 语法会对比 json 解释,以便我们快速理解。

先看一下 yaml 的几个特点:

相比于 JSON 来说,最大的区别是用 缩进 来表示层级,这个和 Python 非常接近。还有强化的一点是支持了注释,JSON 默认是不支持的(虽然 TS 支持),这也对配置文件非常重要。

YAML 支持以下几种数据结构:

先看对象,上一个 json 例子:

转换成 yaml:

对象是最核心的结构,key 值的表示方法是 [key]: ,注意这里 冒号后面有个空格,一定不能少 。value 的值就是一个 纯量 ,且默认不需要引号。

数组和对象的结构差不多,区别是在 key 前用一个 - 符号标识这个是数组项。注意这里 也有一个空格 ,同样也不能少。

转换成 JSON 格式如下:

了解了基本的对象和数组,我们再来看一个复杂的结构。

众所周知,在实际项目配置中很少有简单的对象或数组,大多都是对象和数组相互嵌套而成。在 js 中我们称之为对象数组,而在 yaml 中我们叫 复合结构 。

比如这样一个稍复杂的 JSON:

转换成复合结构的 YAML:

若你想尝试更复杂结构的转换,可以在 这个 网页中在线实践。

纯量比较简单,对应的就是 js 的基本数据类型,支持如下:

比较特殊的两个,null 用 ~ 符号表示,时间大多用 2021-12-21 这种格式表示,如:

转换成 JS 后:

在 yaml 实战过程中,遇到过一些特殊场景,可能需要一些特殊的处理。

在 shell 中我们常见到一些参数很多,然后特别长的命令,如果命令都写在一行的话可读性会非常差。

假设下面的是一条长命令:

在 linux 中可以这样处理:

就是在每行后加 符号标识换行。然而在 YAML 中更简单,不需要加任何符号,直接换行即可:

YAML 默认会把换行符转换成 空格 ,因此转换后 JSON 如下,正是我们需要的:

然而有时候,我们的需求是 保留换行符 ,并不是把它转换成空格,又该怎么办呢?

这个也简单,只需要在首行加一个 | 符号:

转换成 JSON 变成了这样:

获取配置是指,在 YAML 文件中定义的某个配置,如何在代码(JS)里获取?

比如前端在 package.json 里有一个 version 的配置项表示应用版本,我们要在代码中获取版本,可以这么写:

JSON 是可以直接导入的,YAML 可就不行了,那怎么办呢?我们分环境解析:

在浏览器中

浏览器中代码用 webapck 打包,因此加一个 loader 即可:

然后配置 loader:

在组件中使用:

在 Node.js 中

Node.js 环境下没有 Webpack,因此读取 yaml 配置的方法也不一样。

首先安装一个 js-yaml 模块:

然后通过模块提供的方法获取:

配置项复用的意思是,对于定义过的配置,在后面的配置直接引用,而不是再写一遍,从而达到复用的目的。

YAML 中将定义的复用项称为锚点,用 标识;引用锚点则用 * 标识。

对应的 JSON 如下:

但是锚点有个弊端,就是不能作为 变量 在字符串中使用。比如:

此时 key2 的值就是普通字符串 _my name is *name_,引用变得无效了。

其实在实际开发中,字符串中使用变量还是很常见的。比如在复杂的命令中多次使用某个路径,这个时候这个路径就应该是一个变量,在多个命令中复用。

GitHub Action 中有这样的支持,定义一个环境变量,然后在其他的地方复用:

这种实现方式与 webpack 中使用环境变量类似,在构建的时候将变量替换成对应的字符串。

如果本文对你有启发,请甩手一个赞

YAML 配置文件语言

YAML 是专门用来写配置文件的语言,非常简洁和强大,远比 JSON 格式方便。YAML 语言的设计目标,就是方便人类读写。它实质上是一种通用的数据串行化格式。它的基本语法规则如下:

对象的一组键值对,使用冒号结构表示

一组连词线开头的行,构成一个数组

数据结构的子成员是一个数组,则可以在该项下面缩进一个空格

对象和数组可以结合使用,形成复合结构

纯量是最基本的、不可再分的值。以下数据类型都属于 JavaScript 的纯量

课程演示会采用多虚拟机模拟分布式场景,为防止 IP 冲突,无法联网等问题,需要预先设置好主机名、IP、DNS 配置

修改 cloud.cfg 防止重启后主机名还原

修改主机名

编辑 vi /etc/netplan/50-cloud-init.yaml 配置文件,修改内容如下

使用 netplan apply 命令让配置生效


本文标题:go语言yaml配置 go解析yaml文件
转载来源:http://hbruida.cn/article/dodhjhs.html