JSON简介
JavaScript对象符号,简称JSON,是互联网上最流行的数据存储和数据交换的格式之一。JSON语法的简单性使得人类和机器非常容易阅读和书写。
尽管它的名字,JSON数据格式的使用并不限于JavaScript。大多数编程语言实现的数据结构,你可以很容易地转换为JSON,反之亦然。
JavaScript,因此Node.js的运行环境也不例外。更多的时候,这些JSON数据需要从一个文件中读取或写入一个文件中,以便持久化。Node运行环境有内置的fs
模块,专门用于处理文件。
本文将全面介绍如何使用内置的fs
模块来读写JSON格式的数据。我们还将看看一些第三方npm包,它们可以简化JSON格式数据的工作。
序列化和反序列化JSON
序列化是将一个对象或数据结构修改为易于在互联网上存储或传输的格式的过程。你可以通过应用反向过程来恢复序列化的数据。
反序列化指的是将序列化的数据结构转化为其原始格式。
你几乎总是需要在Node中把JSON或JavaScript对象序列化为JSON字符串。在将其写入存储设备或在互联网上传输之前,你可以用JSON.stringify
方法来完成。
const config = { ip: '1234.22.11', port: 3000};
console.log(JSON.stringify(config));
另一方面,在读取JSON文件后,在访问或操作数据之前,你需要用JSON.parse
方法将JSON字符串反序列化为一个普通的JavaScript对象。
const config = JSON.stringify({ ip: '1234.22.11', port: 3000});
console.log(JSON.parse(config));
JSON.stringify
和JSON.parse
是 Node 中全局可用的方法。你不需要在使用前安装或要求。
fs
模块的介绍
因为fs
模块是内置的,你不需要安装它。它提供了一些函数,你可以用它们来读写JSON格式的数据,以及更多。
fs
模块所暴露的每个函数都有同步、回调和基于承诺的形式。一个方法的同步和回调变体可以从同步和回调API中访问。一个函数的基于承诺的变体可以从基于承诺的API中访问。
同步API
内置fs
模块的同步方法阻断事件循环和其余代码的进一步执行,直到操作成功或失败。更多的时候,阻断事件循环不是你想做的事情。
所有同步函数的名称都以Sync
字符结尾。例如,writeFileSync
和readFileSync
都是同步函数。
你可以通过要求fs
来访问同步的API。
const fs = require('fs');
// Blocks the event loop
fs.readFileSync(path, options);
回调API
与阻塞事件循环的同步方法不同,回调API的相应方法是异步的。你会把一个回调函数作为最后一个参数传递给方法。
如果发生错误,回调函数将以Error
对象作为第一个参数被调用。回调函数的其余参数取决于fs
方法。
你也可以像同步API那样通过要求fs
,来访问回调API的方法。
const fs = require('fs');
fs.readFile(path, options, callback);
基于承诺的API
基于承诺的API是异步的,就像回调API一样。它返回一个承诺,你可以通过承诺链或异步等待来管理它。
你可以通过要求fs/promises
来访问基于承诺的API。
const fs = require('fs/promises');
fs.readFile(path)
.then((data) => {
// Do something with the data
})
.catch((error) => {
// Do something if error
});
在上面的代码片段中,我们使用了commonJS语法来访问这些模块。我们将在本文中使用 commonJS 语法,因为 Node 默认将 JavaScript 代码视为 commonJS 模块。如果你愿意,你也可以使用ES6模块。
根据Node文档,内置fs
模块的回调API比基于承诺的API更具有性能。因此,本文中的大多数例子将使用回调API。
如何在Node.js中读取JSON文件
Node运行环境有内置的require
函数和fs
模块,你可以用它来加载或读取JSON文件。因为require
是全局可用的,你不需要要求它。
然而,在使用fs
模块之前,你需要先要求它。我将在下面的小节中讨论如何使用内置的fs
模块和require
函数来读取JSON文件。
如何使用全局require
函数加载JSON文件
你可以使用全局的require
函数来同步加载Node中的JSON文件。在使用require
加载一个文件后,它被缓存了。因此,再次使用require
加载该文件将加载缓存的版本。在服务器环境中,该文件将在下次服务器重启时再次加载。
因此,建议使用require
来加载静态JSON文件,如不经常改变的配置文件。如果你加载的JSON文件不断变化,请不要使用require
,因为它将缓存加载的文件,如果你再次需要相同的文件,就使用缓存的版本。你的最新变化将不会被反映出来。
假设你有一个config.json
文件,内容如下。
{
"port": "3000",
"ip": "127.00.12.3"
}
你可以使用下面的代码在一个JavaScript文件中加载config.json
文件。require
总是将JSON数据加载为一个JavaScript对象。
const config = require('./config.json');
console.log(config);
如何使用fs.readFile
方法读取一个JSON文件
你可以使用readFile
方法来读取JSON文件。它在内存中异步读取整个文件的内容,因此它不是读取大型JSON文件的最理想方法。
readFile
方法需要三个参数。下面的代码片断显示了它的函数签名。
fs.readFile(path, options, callback);
第一个参数,path
,是文件名或文件描述符。第二个参数是一个可选的对象参数,第三个参数是一个callback
函数。你也可以传递一个字符串作为第二个参数而不是一个对象。如果你传递一个字符串,那么它必须被编码。
callback
函数需要两个参数。如果发生错误,第一个参数是error
对象,第二个参数是序列化的JSON数据。
下面的代码片段将读取config.json
文件中的JSON数据,并将其记录在终端。
const fs = require('fs');
fs.readFile('./config.json', 'utf8', (error, data) => {
if(error){
console.log(error);
return;
}
console.log(JSON.parse(data));
})
在你开始处理产生的JavaScript对象之前,请确保对传递给callback
函数的JSON字符串进行反序列化。
如何使用fs.readFileSync
方法读取JSON文件
readFileSync
是另一个在Node中读取文件的内置方法,类似于readFile
。两者之间的区别是,readFile
是异步读取文件,而readFileSync
是同步读取文件。因此,readFileSync
阻止了事件循环和其余代码的执行,直到所有数据都被读取。
要掌握同步和异步代码的区别,你可以在这里阅读 "理解异步JavaScript"一文。
下面是fs.readFileSync
的函数签名。
fs.readFileSync(path, options);
path
是你要读取的JSON文件的路径,你可以传递一个对象作为第二个参数。第二个参数是可选的。
在下面的代码片段中,我们正在使用readFileSync
从config.json
文件中读取JSON数据。
const { readFileSync } = require('fs');
const data = readFileSync('./config.json');
console.log(JSON.parse(data));
如何在Node.js中写到JSON文件
就像读取JSON文件一样,fs
模块提供了内置的方法来写到JSON文件。
你可以使用fs
模块的writeFile
和writeFileSync
方法。两者之间的区别是,writeFile
是异步的,而writeFileSync
是同步的。在写入JSON文件之前,请确保使用JSON.stringify
方法将JavaScript对象序列化为JSON字符串。
如何使用fs.writeFile
方法写入JSON文件
如果你没有向JSON.stringify
方法传递可选的格式化参数,指定如何格式化你的JSON数据,JSON.stringify
将以单行方式格式化你的JSON数据。
如果你传递给writeFile
方法的路径是一个现有的JSON文件,该方法将覆盖指定文件中的数据。如果该文件不存在,它将创建一个新的文件。
const { writeFile } = require('fs');
const path = './config.json';
const config = { ip: '192.0.2.1', port: 3000 };
writeFile(path, JSON.stringify(config, null, 2), (error) => {
if (error) {
console.log('An error has occurred ', error);
return;
}
console.log('Data written successfully to disk');
});
如何使用fs.writeFileSync
方法向JSON文件写入数据
与writeFile
不同,writeFileSync
是同步写入文件的。如果你使用writeFileSync
,你将阻塞事件循环和其他代码的执行,直到操作成功或发生错误。如果你传递的路径不存在,它将创建一个新的文件,如果存在,它将覆盖它。
在下面的代码片断中,我们正在向config.json
文件写入。我们用try-catch
来包装代码,以便我们能够捕捉到任何错误。
const { writeFileSync } = require('fs');
const path = './config.json';
const config = { ip: '192.0.2.1', port: 3000 };
try {
writeFileSync(path, JSON.stringify(config, null, 2), 'utf8');
console.log('Data successfully saved to disk');
} catch (error) {
console.log('An error has occurred ', error);
}
如何追加一个JSON文件
Node没有一个内置的功能来追加或更新现有JSON文件的字段。然而,你可以使用fs
模块的readFile
方法读取JSON文件,更新它,然后用更新的JSON文件覆盖JSON文件。
下面是一个说明如何去做的代码片断。
const { writeFile, readFile } = require('fs');
const path = './config.json';
readFile(path, (error, data) => {
if (error) {
console.log(error);
return;
}
const parsedData = JSON.parse(data);
parsedData.createdAt = new Date().toISOString();
writeFile(path, JSON.stringify(parsedData, null, 2), (err) => {
if (err) {
console.log('Failed to write updated data to file');
return;
}
console.log('Updated file successfully');
});
});
如何使用第三方npm包来读写JSON文件
在本节中,我们将看看最流行的第三方Node包,用于读取和写入JSON格式的数据。
如何使用jsonfile
npm包来读写JSON文件
jsonfile
是一个流行的npm包,用于在Node中读和写JSON文件。你可以使用下面的命令来安装它。
npm i jsonfile
它与fs
模块的readFile
和writeFile
方法类似,尽管jsonfile
比内置方法有一些优势。
这个包的一些特点如下。
- 它可以对JSON进行序列化和反序列化,而且是开箱即用。
- 它有一个内置的工具用于将数据附加到JSON文件中
- 支持承诺链
你可以在下面的代码片段中看到jsonfile
包的运行情况。
const jsonfile = require('jsonfile');
const path = './config.json';
jsonfile.readFile(path, (err, data) => {
if (err) {
console.log(err);
return;
}
console.log(data);
});
你也可以使用承诺链,而不是像上面的例子中那样传递一个回调函数。
const jsonfile = require('jsonfile');
const path = './config.json';
jsonfile
.readFile(path)
.then((data) => {
console.log(data);
})
.catch((err) => {
console.log(err);
});
如何使用fs-extra
npm包来读写JSON文件
fs-extra
是另一个流行的Node包,你可以用它来处理文件。尽管你可以使用这个包来管理JSON文件,但它的方法的功能不仅仅是读写JSON文件。
顾名思义,fs-extra
具有fs
模块提供的所有功能,而且还有更多。根据文档,你可以使用fs-extra
包而不是fs
模块。
在使用它之前,你需要先从npm安装fs-extra
。
npm install fs-extra
下面的代码显示你如何使用fs-extra
包的readJson
方法来读取JSON文件。你可以使用回调函数、承诺链或async/await
。
const fsExtra = require('fs-extra');
const path = './config.json';
// Using callback
fsExtra.readJson(path, (error, config) => {
if (error) {
console.log('An error has occurred');
return;
}
console.log(config);
});
// Using promise chaining
fsExtra
.readJson(path)
.then((config) => {
console.log(config);
})
.catch((error) => {
console.log(error);
});
// Using async/await
async function readJsonData() {
try {
const config = await fsExtra.readJson(path);
console.log(config);
} catch (error) {
console.log(error);
}
}
readJsonData();
下面的代码说明了你如何使用writeJson
方法来写JSON数据。
const { writeJson } = require('fs-extra');
const path = './config.json';
const config = { ip: '192.0.2.1', port: 3000 };
// Using callback
writeJson(path, config, (error) => {
if (error) {
console.log('An error has occurred');
return;
}
console.log('Data written to file successfully ');
});
// Using promise chaining
writeJson(path, config)
.then(() => {
console.log('Data written to file successfully ');
})
.catch((error) => {
console.log(error);
});
// Using async/await
async function writeJsonData() {
try {
await writeJson(path, config);
console.log('Data written to file successfully ');
} catch (error) {
console.log(error);
}
}
writeJsonData();
就像fs
模块一样,fs-extra
也有异步和同步方法。在写到JSON文件之前,你不需要对你的JavaScript对象进行字符串化。
同样地,你也不需要在读取JSON文件后解析成一个JavaScript对象。该模块为你做了开箱即用的工作。
如何使用bfj
npm包来读写JSON文件
bfj
是另一个npm包,你可以用来处理JSON格式的数据。根据文档,它是为管理大型JSON数据集而创建的。
bfj
实现了异步函数,并使用预先分配的固定长度数组,试图缓解与解析和字符串化大型JSON或JavaScript数据集有关的问题 -bfj文档
你可以使用read
方法读取JSON数据。read
方法是异步的,它返回一个承诺。
假设你有一个config.json
文件,你可以使用下面的代码来读取它。
const bfj = require('bfj');
const path = './config.json';
bfj
.read(path)
.then((config) => {
console.log(config);
})
.catch((error) => {
console.log(error);
});
同样地,你可以使用write
方法将数据写入JSON文件。
const bfj = require('bfj');
const path = './config.json';
const config = { ip: '192.0.2.1', port: 3000 };
bfj
.write(path, config)
.then(() => {
console.log('Data has been successfully written to disk');
})
.catch((error) => {
console.log(error);
});
bfj
有很多的功能,你可以在文档中阅读。它是为处理大型JSON数据而特意创建的。它也很慢,所以你应该只在处理相对较大的JSON数据集时使用它。
结论
正如以上章节所解释的,JSON是互联网上最流行的数据交换格式之一。
Node运行环境有内置的fs
模块,你可以用来处理一般的文件。fs
模块有一些方法,你可以使用回调API、基于承诺的API、或同步API来读写JSON文件。
因为回调API的方法比基于承诺的API的方法更有性能,正如文档中所强调的,你最好使用回调API。
除了内置的fs
模块外,还有几个流行的第三方包,如jsonfile
,fs-extra
, 和bfj
。它们有额外的实用功能,使处理JSON文件变得轻而易举。从另一个角度看,你应该评估在你的应用程序中添加第三方包的限制。
The postRead and writing JSON files in Node.js:完整的教程首次出现在LogRocket博客上。