使用Node.js读写JSON文件

Reading and Writing JSON Files with Node.js

在以不同语言编写的应用程序之间交换信息的最佳方法之一是使用JSON(JavaScript对象表示法)格式。 由于其统一性和简单性,JSON几乎完全取代了XML,成为软件中,尤其是Web服务中的标准数据交换格式。

考虑到JSON在软件应用程序(尤其是基于JavaScript的应用程序)中的广泛使用,了解如何在Node.js中读取和写入JSON数据至文件非常重要。 在本文中,我们将解释如何执行这些功能。

读取JSON文件

首先让我们看看如何读取已经创建的文件。 但是在执行此操作之前,我们需要实际创建文件。 在您喜欢的文本编辑器中打开一个新窗口,并向其中添加以下文本:

1
2
3
4
5
6
7
{
   "name":"Sara",
   "age": 23,
   "gender":"Female",
   "department":"History",
   "car":"Honda"
}

现在将此文件另存为" student.json"到您的项目目录。

要从文件读取JSON数据,我们可以使用Node.js fs模块。 此模块中有两个功能可用于从文件系统读取文件:readFilereadFileSync

尽管这两个函数执行相似的任务(即从磁盘读取文件),但不同之处在于这些函数的实际执行方式,我们将在以下各节中详细说明。

使用fs.readFileSync

readFileSync函数以同步方式从文件读取数据。 此功能阻止其余代码执行,直到从文件中读取所有数据为止。 当您的应用程序必须先加载配置设置才能执行任何其他任务时,此功能特别有用。

为了继续我们的示例,让我们使用此函数通过readFileSync函数读取我们先前创建的" student.json"文件。 将以下代码添加到" .js"文件中:

1
2
3
4
5
6
7
'use strict';

const fs = require('fs');

let rawdata = fs.readFileSync('student.json');
let student = JSON.parse(rawdata);
console.log(student);

在上面的Node.js代码中,我们首先将fs模块加载到我们的应用程序中。 接下来,我们使用readFileSync函数,并将相对文件路径传递给我们要读取的文件。 如果将对象rawdata打印到控制台,您将在控制台屏幕上看到原始数据(在Buffer中):

1
<Buffer 7b 20 0a 20 20 20 20 22 6e 61 6d 65 22 3a 20 22 53 61 72 61 22 2c 0a 20 20 20 20 22 61 67 65 22 3a 20 32 33 2c 0a 20 20 20 20 22 67 65 6e 64 65 72 22 ... >

但是,我们要读取JSON格式的文件,而不是原始的十六进制数据。 这是JSON.parse函数起作用的地方。 此函数处理解析原始数据,将其转换为ASCII文本,并将实际的JSON数据解析为JavaScript对象。 现在,如果在控制台上打印student对象,将得到以下输出:

1
2
3
4
5
{ name: 'Sara',
  age: 23,
  gender: 'Female',
  department: 'History',
  car: 'Honda' }

如您所见,我们文件中的JSON已成功加载到student对象中。

使用fs.readFile

在Node.js中读取JSON文件的另一种方法是使用readFile函数。 与readFileSync函数不同,readFile函数以异步方式读取文件数据。 调用readFile函数时,文件读取过程开始,并且控制立即移至执行其余代码行的下一行。 加载文件数据后,此函数将调用提供给它的回调函数。 这样,您就不会在等待操作系统将数据返回给您时阻止代码执行。

在我们的示例中,readFile函数采用两个参数:要读取的文件的路径和完全读取文件后要调用的回调函数。 您还可以选择在选项中包含一个参数,但是本文将不介绍这些参数。

请看以下示例,以了解如何使用readFile函数。

1
2
3
4
5
6
7
8
9
10
11
'use strict';

const fs = require('fs');

fs.readFile('student.json', (err, data) => {
    if (err) throw err;
    let student = JSON.parse(data);
    console.log(student);
});

console.log('This is after the read call');

上面的代码与我们之前的代码段完全一样(带有一个额外的console.log调用),但是它是异步进行的。 以下是您可能已经注意到的一些差异:

  • (err, data) => {}:这是我们的回调函数,在完全读取文件后执行

  • err:由于我们不能轻易将try / catch与异步代码一起使用,因此如果出现问题,该函数将为我们提供err对象。 如果没有错误,则为null

  • 您可能还注意到,我们在调用readFile之后立即将字符串打印到控制台。 这是向您展示异步代码的行为。 执行上述脚本后,您将看到该console.logreadFile回调函数执行之前执行。 这是因为readFile从文件系统读取数据时不会阻止代码执行。

    代码的输出将如下所示:

    1
    2
    3
    4
    5
    6
    This is after the read call
    { name: 'Sara',
      age: 23,
      gender: 'Female',
      department: 'History',
      car: 'Honda' }

    如您所见,文件中的最后一行实际上是在输出中最先显示的那一行。

    使用require

    另一种方法是使用全局require方法来读取和解析JSON文件。 这是用于加载Node模块的相同方法,但也可以用于加载JSON。

    看下面的例子。

    1
    2
    3
    4
    5
    'use strict';

    let jsonData = require('./student.json');

    console.log(jsonData);

    它的工作方式与我们上面显示的readFileSync代码完全相同,但是它是一种全球可用的方法,您可以在任何地方使用它,它具有很多优点。

    但是require函数有一些缺点:

  • Require是同步函数,仅被调用一次,这意味着调用将收到缓存的结果。 如果文件已更新,则无法使用此方法重新读取

  • 您的文件必须具有'.json'扩展名,因此不能灵活。 没有正确的扩展名,require不会将该文件视为JSON文件。

  • 将JSON写入文件

    readFilereadFileSync函数相似,有两个函数可以将数据写入文件:writeFilewriteFileSync。 顾名思义,writeFile方法以异步方式将数据写入文件,而writeFileSync函数以同步方式将数据写入文件。

    我们将在以下各节中仔细研究。

    使用fs.writeFileSync

    writeFileSync函数接受2-3个参数:要向其中写入数据的文件的路径,要写入的数据以及一个可选参数。

    请注意,如果该文件尚不存在,则会为您创建一个新文件。 看下面的例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    'use strict';

    const fs = require('fs');

    let student = {
        name: 'Mike',
        age: 23,
        gender: 'Male',
        department: 'English',
        car: 'Honda'
    };
     
    let data = JSON.stringify(student);
    fs.writeFileSync('student-2.json', data);

    在上面的示例中,我们将JSON对象student存储到名为" student-2.json"的文件中。 注意,这里我们必须在保存数据之前使用JSON.stringify函数。 就像在读取JSON文件时需要将数据解析为JSON格式一样,我们需要先对数据进行"字符串化",然后才能将其以字符串形式存储在文件中。

    执行上面的代码并打开" student-2.json"文件。 您应该在文件中看到以下内容:

    1
    {"name":"Mike","age":23,"gender":"Male","department":"English","car":"Honda"}

    尽管这是我们要写入的数据,但数据采用的是一行字符串的形式,这使我们难以读取。 如果您希望序列化的JSON是人类可读的,则按如下所示更改JSON.Stringify函数:

    1
    let data = JSON.stringify(student, null, 2);

    在这里,我们告诉该方法向序列化的JSON添加换行符和两个缩进。 现在,如果您打开" student-2.json"文件,您将看到以下格式的文本。

    1
    2
    3
    4
    5
    6
    7
    {
     "name":"Mike",
     "age": 23,
     "gender":"Male",
     "department":"English",
     "car":"Honda"
    }

    使用fs.writeFile

    如前所述,writeFile函数以异步方式执行,这意味着在将数据写入文件时不会阻塞我们的代码。 就像以前的异步方法一样,我们需要将回调传递给该函数。

    让我们使用writeFile函数编写另一个文件" student-3.json"。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    'use strict';

    const fs = require('fs');

    let student = {
        name: 'Mike',
        age: 23,
        gender: 'Male',
        department: 'English',
        car: 'Honda'
    };
     
    let data = JSON.stringify(student, null, 2);

    fs.writeFile('student-3.json', data, (err) => {
        if (err) throw err;
        console.log('Data written to file');
    });

    console.log('This is after the write call');

    上面脚本的输出将是:

    1
    2
    This is after the write call
    Data written to file

    再一次,您可以看到我们的代码的最后一行实际上首先显示在控制台中,因为尚未调用我们的回调。 如果您有大量数据要写入文件,或者有许多文件要写入,这将节省大量执行时间。

    学到更多

    想更多地了解Node.js的基础知识? 就个人而言,我建议您参加在线课程,例如Wes Bos的LearnNode.js。 您不仅会学习最新的ES2017语法,而且还将构建完整的餐厅应用程序。 以我的经验,像这样构建现实世界的应用程序是最快的学习方法。

    结论

    在整篇文章中,我们展示了如何在文件之间读取和写入JSON数据,这是了解作为Web程序员时如何做的一项非常常见且重要的任务。

    fs模块中有两种方法可以读取和写入JSON文件。 readFilereadFileSync函数将分别以异步和同步方式从文件读取JSON数据。 您还可以使用全局require方法以单行代码处理从文件读取/解析JSON数据。 但是,require是同步的,并且只能从扩展名为'.json'的文件中读取JSON数据。

    同样,fs模块中的writeFilewriteFileSync函数分别以异步和同步方式将JSON数据写入文件。