在Node.js / JavaScript中将YAML读取和写入文件

Reading and Writing YAML to a File in Node.js/JavaScript

介绍

在过去的几年中,YAML代表" YAML不是标记语言",已经非常流行用于以序列化方式存储数据,通常是配置文件。 由于YAML本质上是一种数据格式,因此YAML规范相当简短。 因此,YAML库唯一需要的功能就是解析或生成YAML格式文件的能力。

在本文中,我们将了解如何将YAML与Node.js应用程序一起使用。 我们将首先了解如何将数据存储在YAML文件中,然后将这些数据加载到JavaScript对象中。 最后,我们将学习如何在YAML文件中存储JavaScript对象。

有一些流行的Node库用于解析和生成YAML:yaml和js-yaml。 js-yaml在这两个库中更受欢迎,因此我们将在本文中重点介绍。

在继续进行之前,本教程有一些先决条件。 您应该对JavaScript的语法有基本的了解,尤其是对于Node.js运行时。 您还需要在系统上安装Node.js和NPM进行后续操作。 除此之外,本教程对于初学者而言非常简单且易于遵循。

安装

与任何Node软件包一样,使用NPM进行安装非常简单:

1
$ npm install js-yaml

只要确保使用正确的标志将依赖项保存在package.json文件中即可。 例如,如果js-yaml仅用于开发目的,则使用--save-dev,否则使用--save(如果它在应用的生产环境中使用)。

您可以通过从同一目录打开REPL并使用以下内容导入软件包来验证其是否正确安装:

1
2
3
$ node
> const yaml = require('js-yaml')
>

js-yaml库还支持CLI使用,它使您可以从命令行检查YAML文件。 您可以通过全局安装软件包来启用此功能:

1
$ npm install -g js-yaml

在Node.js中读取YAML文件

我们将从读取YAML格式的文件并将其解析为JS对象开始。 就本教程而言,假设我们有一个文件data.yaml,其内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
--- # Article data
  title:"Reading and Writing YAML to a File in Node.js/JavaScript"
  url path:"/reading-and-writing-yaml-to-a-file-in-node-js-javascript"
  domain:"stackabuse.com"
  port: 443
  is-https: true
  meta:
    published-at:"Nov. 1st, 2019"
    author:
      name:"Scott Robinson"
      contact:"<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="2457474b505064575045474f45465157410a474b49">[emailprotected]</a>"
    tags:
      - javascript
      - node.js
      - web development

要读取和解析此文件,我们将使用.safeLoad()方法:

1
2
3
4
5
6
7
8
9
10
11
12
// read.js
const fs = require('fs');
const yaml = require('js-yaml');

try {
    let fileContents = fs.readFileSync('./data.yaml', 'utf8');
    let data = yaml.safeLoad(fileContents);

    console.log(data);
} catch (e) {
    console.log(e);
}

运行此代码将输出以下内容:

1
2
3
4
5
6
7
8
9
10
$ node read.js
{ title: 'Reading and Writing YAML to a File in Node.js/JavaScript',
  'url path': '/reading-and-writing-yaml-to-a-file-in-node-js-javascript',
  domain: 'stackabuse.com',
  port: 443,
  'is-https': true,
  meta:
   { 'published-at': 'Nov. 1st, 2019',
     author: { name: 'Scott Robinson', contact: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="d9aabab6adad99aaadb8bab2b8bbacaabcf7bab6b4">[emailprotected]</a>' },
     tags: [ 'javascript', 'node.js', 'web development' ] } }

您可以看到,YAML文件中的数据现在已转换为与文件相同结构的JS文字和对象。

建议使用.safeLoad()方法来解析YAML内容,因为它对于不受信任的数据是安全的。 值得注意的一个限制是该方法不支持多文档源。 如果您熟悉YAML,就会知道YAML可以在单个文件中包含多个"文档",这些文件用---语法分隔。 例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
--- # Programming language
  language:"JavaScript"
  created-at:"December 4, 1995"
  domain:"stackabuse.com"
  creator:"Brendan Eich"
--- # Website
  domain:"wikipedia.org"
  created-at:"January 15, 2001"
  num-languages: 304
  num-articles: 51360771
  creator:
    - Jimmy Wales
    - Larry Sanger

.safeLoad()加载此文件将引发异常。 相反,应使用.safeLoadAll()方法,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
// read-all.js
const fs = require('fs');
const yaml = require('js-yaml');

try {
    let fileContents = fs.readFileSync('./data-multi.yaml', 'utf8');
    let data = yaml.safeLoadAll(fileContents);

    console.log(data);
} catch (e) {
    console.log(e);
}

这将导致一系列已解析的YAML文档:

1
2
3
4
5
6
7
8
9
10
$ node read-all.js
[ { language: 'JavaScript',
    'created-at': 'December 4, 1995',
    domain: 'stackabuse.com',
    creator: 'Brendan Eich' },
  { domain: 'wikipedia.org',
    'created-at': 'January 15, 2001',
    'num-languages': 304,
    'num-articles': 51360771,
    creator: [ 'Jimmy Wales', 'Larry Sanger' ] } ]

另一个值得一提的方法是.load()方法,该方法与.safeLoad()非常相似,不同之处在于它支持所有YAML模式类型。 额外受支持的类型特定于JavaScript(!!js/undefined!!js/regexp!!js/function),并且您必须绝对信任这些YAML文件中的数据,因为它可以加载不受信任的代码。

例如,可以在YAML中定义一个函数,如下所示:

1
'toString': !<tag:yaml.org,2002:js/function> function() {console.log('Malicious code execuited!');}

该标记告诉我们的YAML库将其解析为一个函数,然后可以在以后执行。 正如文档中指出的那样,在JS对象上执行的一种常见方法是toString,我们可以像这样利用它:

1
2
3
4
5
6
// unsafe.js
const yaml = require('js-yaml');

let yamlStr ="'toString': !<tag:yaml.org,2002:js/function> function() {console.log('Malicious code execuited!');}";
let loadedYaml = yaml.load(yamlStr) + '';
console.log(loadedYaml);

运行此代码表明console.log方法确实已执行:

1
2
3
$ node unsafe.js
Malicious code execuited!
undefined

在Node.js中将YAML写入文件

现在,您知道如何使用Node.js读取YAML文件,让我们看看如何将JavaScript对象/数据写入YAML文件。

对于此示例,我们将使用以下JS对象,您可以从前面的示例中识别出以下对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let data = {
    title: 'Reading and Writing YAML to a File in Node.js/JavaScript',
    'url path': '/reading-and-writing-yaml-to-a-file-in-node-js-javascript',
    domain: 'stackabuse.com',
    port: 443,
    'is-https': true,
    meta: {
        'published-at': 'Nov. 1st, 2019',
        author: {
            name: 'Scott Robinson',
            contact: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="d3a0b0bca7a793a0a7b2b0b8b2b1a6a0b6fdb0bcbe">[emailprotected]</a>'
        },
        tags: [
            'javascript', 'node.js', 'web development'
        ]
    }
};

为了序列化该对象并将其保存到YAML格式的文件中,我们将使用.safeDump()方法,该方法再次使用js-yamlDEFAULT_SAFE_SCHEMA

1
2
3
4
5
6
7
8
// write.js
const fs = require('fs');
const yaml = require('js-yaml');

let data = { /* Same as above */};

let yamlStr = yaml.safeDump(data);
fs.writeFileSync('data-out.yaml', yamlStr, 'utf8');

执行此代码将写出一个如下所示的YAML文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
title: Reading and Writing YAML to a File in Node.js/JavaScript
url path: /reading-and-writing-yaml-to-a-file-in-node-js-javascript
domain: stackabuse.com
port: 443
is-https: true
meta:
  published-at: 'Nov. 1st, 2019'
  author:
    name: Scott Robinson
    contact: <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="ea9989859e9eaa999e8b89818b889f998fc4898587">[emailprotected]</a>
  tags:
    - javascript
    - node.js
    - web development

此输出与我们在本文前面阅读的原始YAML几乎相同,除了不包含文档分隔符(---)。

资料类型

请记住,并非所有JavaScript数据类型都可以直接序列化为YAML,反之亦然,这一点很重要。 在某些情况下,如果不直接支持,则将使用最接近的数据类型。 例如,YAML !!seq类型将被解析为JavaScript数组。

根据js-yaml文档,支持以下数据类型:

如前所述,也可以支持其他特定于JS的类型,但前提是您不使用"安全"方法。

结论

YAML是一种越来越流行的格式,用于为应用程序结构化数据,通常作为配置文件,也可以替代使用JSON的任何内容。 由于它的高度灵活性和易于阅读的语法,它在许多项目中都迅速取代了JSON,尽管两者仍然占有一席之地。

在本文中,我们展示了如何使用js-yaml库将YAML文件解析为Node.js中的JavaScript对象,以及如何将JavaScript对象序列化为YAML文件。 我们还展示了哪些JS类型用于各种YAML数据类型。 有关此库的更多详细信息,请查阅官方文档。