【发布时间】:2011-08-09 06:18:46
【问题描述】:
我应该如何使用 Node.js 解析 JSON?是否有一些模块可以安全地验证和解析 JSON?
【问题讨论】:
标签: javascript json node.js
我应该如何使用 Node.js 解析 JSON?是否有一些模块可以安全地验证和解析 JSON?
【问题讨论】:
标签: javascript json node.js
您可以简单地使用JSON.parse。
JSON 对象is part of the ECMAScript 5 specification 的定义。 node.js 建立在 Google Chrome 的 V8 引擎之上,该引擎遵循 ECMA 标准。因此,node.js 也有一个全局对象JSON[docs]。
注意 - JSON.parse 可以占用当前线程,因为它是一种同步方法。因此,如果您打算解析大型 JSON 对象,请使用流式 json 解析器。
【讨论】:
你可以require.json 文件。
var parsedJSON = require('./file-name');
例如,如果您在与源代码文件相同的目录中有一个config.json 文件,您将使用:
var config = require('./config.json');
或(文件扩展名可以省略):
var config = require('./config');
请注意,require 是同步的,并且只读取文件一次,随后的调用从缓存中返回结果
另请注意,您应该只将它用于您绝对控制下的本地文件,因为它可能会执行文件中的任何代码。
【讨论】:
require 是同步的。如果你想友好地异步使用 fs.readFile 而不是 JSON.parse
.json 扩展名!如果您的文件没有.json 扩展名,require 不会将其视为 json 文件。
您可以使用JSON.parse()。
您应该能够在任何与 ECMAScript 5 兼容的 JavaScript 实现上使用 JSON 对象。而构建 Node.js 的 V8 就是其中之一。
注意:如果您使用 JSON 文件来存储敏感信息(例如密码),那是错误的做法。看看 Heroku 是如何做到的:https://devcenter.heroku.com/articles/config-vars#setting-up-config-vars-for-a-deployed-application。了解您的平台是如何做到的,并使用
process.env从代码中检索配置变量。
var str = '{ "name": "John Doe", "age": 42 }';
var obj = JSON.parse(str);
您必须对fs 模块进行一些文件操作。
var fs = require('fs');
fs.readFile('/path/to/file.json', 'utf8', function (err, data) {
if (err) throw err; // we'll not consider error handling for now
var obj = JSON.parse(data);
});
var fs = require('fs');
var json = JSON.parse(fs.readFileSync('/path/to/file.json', 'utf8'));
require?再想想!You can sometimes use require:
var obj = require('path/to/file.json');
但是,出于以下几个原因,我不建议这样做:
require 是同步的。如果你有一个非常大的 JSON 文件,它会阻塞你的事件循环。您确实需要将JSON.parse 与fs.readFile 一起使用。require 将只读取一次文件。随后对同一文件的require 调用将返回一个缓存副本。如果您想阅读不断更新的.json 文件,这不是一个好主意。你可以使用a hack。但是此时,简单地使用fs 会更容易。.json 扩展名,require 不会将文件内容视为 JSON。说真的!使用JSON.parse。
load-json-file模块如果您正在阅读大量的.json 文件(并且如果您非常懒惰),那么每次都编写样板代码会变得很烦人。您可以使用load-json-file 模块保存一些字符。
const loadJsonFile = require('load-json-file');
loadJsonFile('/path/to/file.json').then(json => {
// `json` contains the parsed object
});
let obj = loadJsonFile.sync('/path/to/file.json');
如果 JSON 内容通过网络流式传输,则需要使用流式 JSON 解析器。否则它将占用您的处理器并阻塞您的事件循环,直到 JSON 内容完全流式传输。
这里有plenty of packages available in NPM。选择最适合您的。
如果您不确定传递给JSON.parse() 的内容是否为valid JSON,请确保将对JSON.parse() 的调用包含在try/catch 块中。用户提供的 JSON 字符串可能会使您的应用程序崩溃,甚至可能导致安全漏洞。如果您解析外部提供的 JSON,请确保完成错误处理。
【讨论】:
and could even lead to security holes 出于好奇,怎么样?
<script>... 的文本,并且错误溢出到客户端,那么您就有一个 XSS 错误。因此,IMO 在您解析它的地方处理 JSON 错误非常重要。
require 包含 JSON 不是很酷吗?”甚至没有费心记录副作用。这也意味着 require 接受两种语言的文件:JavaScript 和 JSON(不,它们不一样)。 SRP 就这么多。
使用JSON object:
JSON.parse(str);
【讨论】:
JSON.parse 的另一个例子:
var fs = require('fs');
var file = __dirname + '/config.json';
fs.readFile(file, 'utf8', function (err, data) {
if (err) {
console.log('Error: ' + err);
return;
}
data = JSON.parse(data);
console.dir(data);
});
【讨论】:
我想提一下,全局 JSON 对象还有其他选择。
JSON.parse 和 JSON.stringify 都是同步的,因此如果您想处理大对象,您可能需要查看一些异步 JSON 模块。
看看:https://github.com/joyent/node/wiki/Modules#wiki-parsers-json
【讨论】:
JSON.parse 正在解析格式错误的 JSON,您的整个应用程序将会崩溃,或者使用 process.on('uncaughtException', function(err) { ... });,最终将没有机会向用户发送“格式错误的 JSON”错误。
async 解析器是哪一个?我没找到。
包括node-fs 库。
var fs = require("fs");
var file = JSON.parse(fs.readFileSync("./PATH/data.json", "utf8"));
有关“fs”库的更多信息,请参阅http://nodejs.org/api/fs.html 上的文档
【讨论】:
由于你不知道你的字符串实际上是有效的,我会先把它放到一个 try catch 中。另外,由于 try catch 块没有被节点优化,我会把整个东西放到另一个函数中:
function tryParseJson(str) {
try {
return JSON.parse(str);
} catch (ex) {
return null;
}
}
OR 在“异步风格”
function tryParseJson(str, callback) {
process.nextTick(function () {
try {
callback(null, JSON.parse(str));
} catch (ex) {
callback(ex)
}
})
}
【讨论】:
解析 JSON 流?使用JSONStream。
var request = require('request')
, JSONStream = require('JSONStream')
request({url: 'http://isaacs.couchone.com/registry/_all_docs'})
.pipe(JSONStream.parse('rows.*'))
.pipe(es.mapSync(function (data) {
return data
}))
【讨论】:
这里的每个人都谈到了 JSON.parse,所以我想说点别的。有一个很棒的模块 Connect 带有许多中间件,可以使应用程序的开发更容易和更好。其中一个中间件是bodyParser。它解析 JSON、html-forms 等。还有一个特定的中间件仅用于 JSON 解析noop。
看看上面的链接,它可能对你很有帮助。
【讨论】:
JSON.parse("your string");
就是这样。
【讨论】:
正如这里的其他答案所提到的,您可能需要一个您知道是安全且存在的本地 json 文件,例如配置文件:
var objectFromRequire = require('path/to/my/config.json');
或使用全局 JSON 对象将字符串值解析为对象:
var stringContainingJson = '\"json that is obtained from somewhere\"';
var objectFromParse = JSON.parse(stringContainingJson);
请注意,当您需要一个文件时,会评估该文件的内容,这会带来安全风险,以防它不是 json 文件而是 js 文件。
在这里,我发布了一个演示,您可以在其中查看这两种方法并在线使用它们(解析示例在 app.js 文件中 - 然后单击运行按钮并在终端中查看结果): http://staging1.codefresh.io/labs/api/env/json-parse-example
你可以修改代码看看效果...
【讨论】:
在 Node.js 中使用 JSON 进行配置?阅读本文,让您的配置技能超过 9000...
注意:人们声称 data = require('./data.json');是一个 安全风险和热心地拒绝人们的答案:您完全错。 尝试在该文件中放置非 JSON...Node 会给你一个错误,exactly 就像你用 much 更慢更难的代码做同样的事情一样手动文件读取,然后是 JSON.parse()。请停止传播错误信息;你在伤害世界,而不是在帮助。 Node 被设计允许这样做; 这不是安全风险!
适当的应用程序有 3 个以上的层配置:
大多数开发人员将他们的服务器和应用配置视为可以更改。它不能。您可以层级更改从更高层彼此叠加,但您正在修改基本要求。有些东西需要存在!让你的配置看起来是不可变的,因为其中一些基本上是,就像你的源代码一样。
看不到你的很多东西在启动后不会改变会导致反模式,比如用 try/catch 块乱扔你的配置加载,假装你可以继续没有你的正常设置应用程序。你不能。如果可以,那属于社区/用户配置层,而不是服务器/应用程序配置层。你只是做错了。当应用程序完成它的引导时,可选的东西应该放在最上面。
别再用头撞墙了:你的配置应该超简单。
看看使用简单的 json 配置文件和简单的 app.js 文件来设置与协议无关和数据源无关的服务框架这样复杂的东西是多么容易......
container-config.js...
{
"service": {
"type" : "http",
"name" : "login",
"port" : 8085
},
"data": {
"type" : "mysql",
"host" : "localhost",
"user" : "notRoot",
"pass" : "oober1337",
"name" : "connect"
}
}
index.js...(驱动一切的引擎)
var config = require('./container-config.json'); // Get our service configuration.
var data = require(config.data.type); // Load our data source plugin ('npm install mysql' for mysql).
var service = require(config.service.type); // Load our service plugin ('http' is built-in to node).
var processor = require('./app.js'); // Load our processor (the code you write).
var connection = data.createConnection({ host: config.data.host, user: config.data.user, password: config.data.pass, database: config.data.name });
var server = service.createServer(processor);
connection.connect();
server.listen(config.service.port, function() { console.log("%s service listening on port %s", config.service.type, config.service.port); });
app.js...(支持协议无关和数据源无关服务的代码)
module.exports = function(request, response){
response.end('Responding to: ' + request.url);
}
使用此模式,您现在可以在启动的应用程序上加载社区和用户配置内容,开发操作已准备好将您的工作推入容器并对其进行扩展。你读的是多租户。用户空间是孤立的。您现在可以将您正在使用的服务协议、您正在使用的数据库类型的关注点分开,并专注于编写好的代码。
因为您使用的是层,所以您可以在任何时候(分层的配置对象)为所有事情依赖单一的事实来源,并且避免在每一步都进行错误检查,担心“哦,废话,我该怎么做让 this 在没有适当配置的情况下工作?!?”。
【讨论】:
我的解决方案:
var fs = require('fs');
var file = __dirname + '/config.json';
fs.readFile(file, 'utf8', function (err, data) {
if (err) {
console.log('Error: ' + err);
return;
}
data = JSON.parse(data);
console.dir(data);
});
【讨论】:
TypeError: path must be a string or Buffer 错误 - 知道从哪里开始调试此问题吗?
只是想完成答案(因为我挣扎了一段时间),想展示如何访问json信息,这个例子展示了访问Json Array:
var request = require('request');
request('https://server/run?oper=get_groups_joined_by_user_id&user_id=5111298845048832', function (error, response, body) {
if (!error && response.statusCode == 200) {
var jsonArr = JSON.parse(body);
console.log(jsonArr);
console.log("group id:" + jsonArr[0].id);
}
})
【讨论】:
只是为了让它尽可能复杂,并尽可能多地引入包......
const fs = require('fs');
const bluebird = require('bluebird');
const _ = require('lodash');
const readTextFile = _.partial(bluebird.promisify(fs.readFile), _, {encoding:'utf8',flag:'r'});
const readJsonFile = filename => readTextFile(filename).then(JSON.parse);
这可以让你做到:
var dataPromise = readJsonFile("foo.json");
dataPromise.then(console.log);
或者,如果您使用的是 async/await:
let data = await readJsonFile("foo.json");
与仅使用 readFileSync 相比的优势在于,您的 Node 服务器可以在从磁盘读取文件时处理其他请求。
【讨论】:
如果您需要使用 Node.js 以安全的方式解析 JSON(又名:用户可以输入数据或公共 API),我建议您使用 secure-json-parse。
用法类似于默认的JSON.parse,但它会保护您的代码免受:
const badJson = '{ "a": 5, "b": 6, "__proto__": { "x": 7 }, "constructor": {"prototype": {"bar": "baz"} } }'
const infected = JSON.parse(badJson)
console.log(infected.x) // print undefined
const x = Object.assign({}, infected)
console.log(x.x) // print 7
const sjson = require('secure-json-parse')
console.log(sjson.parse(badJson)) // it will throw by default, you can ignore malicious data also
【讨论】:
JSON.parse 不会确保您正在解析的 json 字符串的安全性。你应该看看像json-safe-parse 这样的库或类似的库。
来自 json-safe-parse npm 页面:
JSON.parse 很棒,但它在 JavaScript 上下文中存在一个严重缺陷:它允许您覆盖继承的属性。如果您从不受信任的来源(例如:用户)解析 JSON,并在其上调用您期望存在的函数,这可能会成为一个问题。
【讨论】:
利用 Lodash 的尝试函数返回一个错误对象,您可以使用 isError 函数进行处理。
// Returns an error object on failure
function parseJSON(jsonString) {
return _.attempt(JSON.parse.bind(null, jsonString));
}
// Example Usage
var goodJson = '{"id":123}';
var badJson = '{id:123}';
var goodResult = parseJSON(goodJson);
var badResult = parseJSON(badJson);
if (_.isError(goodResult)) {
console.log('goodResult: handle error');
} else {
console.log('goodResult: continue processing');
}
// > goodResult: continue processing
if (_.isError(badResult)) {
console.log('badResult: handle error');
} else {
console.log('badResult: continue processing');
}
// > badResult: handle error
【讨论】:
.bind 而不是仅仅使用 _.attempt(JSON.parse, str)
始终确保在 try catch 块中使用 JSON.parse,因为如果您的 json 中有一些损坏的数据,节点总是会抛出意外错误,因此请使用此代码而不是简单的 JSON.Parse
try{
JSON.parse(data)
}
catch(e){
throw new Error("data is corrupted")
}
【讨论】:
如上述答案中提到的,我们可以使用JSON.parse() 将字符串解析为JSON
但在解析之前,请务必解析正确的数据,否则可能会导致整个应用程序崩溃
这样使用是安全的
let parsedObj = {}
try {
parsedObj = JSON.parse(data);
} catch(e) {
console.log("Cannot parse because data is not is proper json format")
}
【讨论】:
如果您想在 JSON 中添加一些 cmets 并允许尾随逗号,您可能需要使用以下实现:
var fs = require('fs');
var data = parseJsData('./message.json');
console.log('[INFO] data:', data);
function parseJsData(filename) {
var json = fs.readFileSync(filename, 'utf8')
.replace(/\s*\/\/.+/g, '')
.replace(/,(\s*\})/g, '}')
;
return JSON.parse(json);
}
请注意,如果您的 JSON 中有 "abc": "foo // bar" 之类的内容,它可能无法正常工作。所以YMMV。
【讨论】:
如果 JSON 源文件很大,可能需要考虑通过 Node.js 8.0 的原生 async / await 方法的异步路由,如下所示
const fs = require('fs')
const fsReadFile = (fileName) => {
fileName = `${__dirname}/${fileName}`
return new Promise((resolve, reject) => {
fs.readFile(fileName, 'utf8', (error, data) => {
if (!error && data) {
resolve(data)
} else {
reject(error);
}
});
})
}
async function parseJSON(fileName) {
try {
return JSON.parse(await fsReadFile(fileName));
} catch (err) {
return { Error: `Something has gone wrong: ${err}` };
}
}
parseJSON('veryBigFile.json')
.then(res => console.log(res))
.catch(err => console.log(err))
【讨论】:
我使用fs-extra。我非常喜欢它,因为 - 尽管它支持回调 - 它也支持 Promises。所以它只是让我能够以更易读的方式编写我的代码:
const fs = require('fs-extra');
fs.readJson("path/to/foo.json").then(obj => {
//Do dome stuff with obj
})
.catch(err => {
console.error(err);
});
它还有许多标准fs模块没有提供的有用方法和,最重要的是,它还桥接了原生fs模块的方法并承诺它们.
注意:您仍然可以使用本机 Node.js 方法。它们被承诺并复制到 fs-extra。请参阅
fs.read()和fs.write()上的注释
所以基本上都是优点。我希望其他人觉得这很有用。
【讨论】:
使用JSON.parse(str);。阅读更多关于它的信息here。
这里有一些例子:
var jsonStr = '{"result":true, "count":42}';
obj = JSON.parse(jsonStr);
console.log(obj.count); // expected output: 42
console.log(obj.result); // expected output: true
【讨论】:
您可以使用 JSON.parse()(这是一个内置函数,可能会强制您使用 try-catch 语句包装它)。
或者使用一些 JSON 解析 npm 库,比如json-parse-or
【讨论】:
为了安全起见,使用它
var data = JSON.parse(Buffer.concat(arr).toString());
【讨论】:
NodeJs 是一个基于 JavaScript 的服务器,因此您可以像在纯 JavaScript 中那样做...
假设你在 NodeJs 中有这个 Json...
var details = '{ "name": "Alireza Dezfoolian", "netWorth": "$0" }';
var obj = JSON.parse(details);
您可以按照上面的操作来获取您的 json 的解析版本...
【讨论】:
不需要其他模块。
只需使用var parsedObj = JSON.parse(yourObj);
我认为这有任何安全问题
【讨论】:
很简单,可以使用JSON.stringify(json_obj)将JSON转为字符串,使用JSON.parse("your json string")将字符串转为JSON。
【讨论】: