解析“宽松”的 JSON,而不使用 eval

Parsing "relaxed" JSON without eval

提问人:axkibe 提问时间:3/10/2012 最后编辑:Boannaxkibe 更新时间:5/6/2023 访问量:41847

问:

解析“宽松”JSON但避免邪恶的最简单方法是什么?eval

以下代码会引发错误:

JSON.parse("{muh: 2}");

因为正确的 JSON 应该有引号的键:{"muh": 2}


我的用例是一个简单的测试接口,我用它来将 JSON 命令编写到我的节点服务器。到目前为止,我只是使用它,因为它只是一个测试应用程序。但是,在整个项目中使用 JSHint 一直困扰着我。因此,我想要一个安全的替代方案,它仍然允许宽松的键语法。evaleval

PS:我不想为了测试应用程序而自己编写解析器:-)

JavaScript JSON

评论

3赞 bfavaretto 3/10/2012
如果它是一个测试应用程序,并且您对 JSON 输入有绝对控制权,那么仅使用 .eval
0赞 bfavaretto 3/10/2012
另一种选择是使用适当的 JSON plus 。除此之外,我想它是 eval 或编写您自己的解析器。JSON.parse
9赞 hspain 3/10/2012
@bfavaretto 这很危险。我们都知道“测试”代码进入生产环境的次数。您不妨从安全的基础开始。
0赞 bfavaretto 3/10/2012
@hspain,我知道。我认为这里最好的办法是首先使用正确的 JSON。“宽松”的JSON也不应该投入生产,对吧?
0赞 lcapra 3/10/2012
试着看看这个: code.google.com/p/jquery-json

答:

7赞 kennebec 3/10/2012 #1

如果在编写字符串时不能引用键,则可以在使用 JSON.parse- 之前插入引号

var s= "{muh: 2,mah:3,moh:4}";
s= s.replace(/([a-z][^:]*)(?=\s*:)/g, '"$1"');

var o= JSON.parse(s);
/*  returned value:[object Object] */
JSON.stringify(o)
/*  returned value: (String){
    "muh":2, "mah":3, "moh":4
}

评论

1赞 axkibe 3/10/2012
> '{muh: “foo”,mah:3,moh:4}'.replace(/([a-z][^:]*)(?=\s*:)/g, '“$1”');'{“muh”: “”foo“,mah”:3,“moh”:4}' 我一直在思考这个问题,但看到这个例子,它并没有完全削减它。它有点复杂。
1赞 Jan Grz 6/8/2016
尝试在无效的 JSON 中插入引号,如下所示:结果将是"{muh: true, notMuch: 123, pika:{pika:\"chu\"}}"{"muh": "true, notMuch": 123, "pika":{"pika":"chu"}}
1赞 alevkon 7/24/2016
您忘记将 /i 修饰符包含在 RegExp 中,如下所示:s= s.replace(/([a-z][^:]*)(?=\s*:)/gi, '"$1"');
0赞 Enkay 10/13/2016
感谢您实际回答这个问题!
34赞 Aseem Kishore 5/28/2012 #2

你已经知道这一点,因为你把我带到了这里,但我认为在这里记录下来可能会很好:

长期以来,我一直渴望能够编写仍然有效的 JS 的“宽松”JSON,所以我采用了 Douglas Crockford 的免评估json_parse.js,并将其扩展为支持 ES5 功能:

https://github.com/aseemk/json5

此模块在 npm 上可用,可用作本机方法的直接替代品。(其输出为常规 JSON。JSON.parse()stringify()

希望这有帮助!

评论

1赞 leumasme 5/25/2020
由于“生命周期结束”的原因,文件在提交中删除。从提交历史记录链接到文件:github.com/douglascrockford/JSON-js/blob/...
43赞 Arnaud Weil 1/13/2016 #3

您可以使用正则表达式 replace 来清理 JSON:

var badJson = "{muh: 2}";
var correctJson = badJson.replace(/(['"])?([a-z0-9A-Z_]+)(['"])?:/g, '"$2": ');
JSON.parse(correctJson);

评论

2赞 Jan Grz 6/8/2016
此正则表达式有效,@kennebec正则表达式不适用于包含布尔值的 JSONvalue
2赞 Malvineous 8/20/2016
如果您在之前添加,您还可以修复冒号前有空格的 JSON 字符串,例如\s*:/g{muh : 2}
7赞 Malvineous 8/20/2016
刚刚意识到这个正则表达式对于包含冒号的值失败,例如{muh: "2:30pm"}
0赞 driconmax 2/16/2017
如果字符串包含日期,这将不起作用。(15/2/2017 17:00:00)
5赞 Felix Dombek 7/6/2017
不适用于任何包含(URL、引文等)的字符串,但仍然为我节省了一些工作。:
17赞 Malvineous 8/20/2016 #4

这就是我最终不得不做的。我扩展了@ArnaudWeil的答案,并添加了对出现在值中的支持::

var badJSON = '{one : "1:1", two : { three: \'3:3\' }}';

var fixedJSON = badJSON

	// Replace ":" with "@colon@" if it's between double-quotes
	.replace(/:\s*"([^"]*)"/g, function(match, p1) {
		return ': "' + p1.replace(/:/g, '@colon@') + '"';
	})

	// Replace ":" with "@colon@" if it's between single-quotes
	.replace(/:\s*'([^']*)'/g, function(match, p1) {
		return ': "' + p1.replace(/:/g, '@colon@') + '"';
	})

	// Add double-quotes around any tokens before the remaining ":"
	.replace(/(['"])?([a-z0-9A-Z_]+)(['"])?\s*:/g, '"$2": ')

	// Turn "@colon@" back into ":"
	.replace(/@colon@/g, ':')
;

console.log('Before: ' + badJSON);
console.log('After: ' + fixedJSON);
console.log(JSON.parse(fixedJSON));

它产生以下输出:

Before: {one : "1:1", two : { three: '3:3' }}
After: {"one":  "1:1", "two":  { "three":  "3:3" }}
{
  "one": "1:1",
  "two": {
    "three": "3:3"
  }
}

评论

4赞 8/20/2016
这几乎可以保证在未来的某些边缘情况下会中断。
2赞 Malvineous 8/20/2016
@torazaburo:当然,但当这种情况发生时,也许有人可以在它的基础上解决问题,就像我在早期解决方案的基础上解决我发现的问题一样。
0赞 8/20/2016
一种奇怪的编码方法——编写你知道可能会损坏的代码,并计划稍后修复它。
7赞 Malvineous 8/20/2016
@torazaburo:你可能认为它会崩溃,但我正在处理一个不会改变的遗留系统,所以对我来说,它不比任何其他代码更容易崩溃。如果你认为这个答案太糟糕了,为什么不贡献一个更好的答案呢?我想看看您打算如何以一种不会在未来的边缘情况下中断的方式解决这个问题。
0赞 opcode 5/14/2017
这对我有用,但是如果我们能对每行的作用发表一些评论,那就太好了。只是为了我们这些不是正则表达式专家的道德。
2赞 Steven Spungin 2/3/2018 #5

您还可以使用 Flamenco 的 really-relaxed-json (https://www.npmjs.com/package/really-relaxed-json),它更进一步,不允许使用逗号、悬空逗号、注释、多行字符串等。

这是规范 http://www.relaxedjson.org

还有一些在线解析器:
http://www.relaxedjson.org/docs/converter.html

预加载了“bad json”

{one : "1:1", two : { three: '3:3' }}

错误的 JSON

预装了“更糟糕的 json”(没有逗号)

{one : '1:1' two : { three: '3:3' }}

更差的 JSON

预加载了“可怕的 json”(没有逗号、没有引号和转义冒号)

{one : 1\:1 two : {three : 3\:3}}

可怕的 JSON

评论

10赞 Bogdan Mart 8/13/2018
真的很诡异的库,GitHub 上没有代码,不知道包里面有什么以及它如何改变
0赞 Austin Burk 6/10/2020
这个库几乎是我需要的,除了 npm 模块是一个预构建/捆绑的库,因此几乎无法修改。不幸:(
0赞 Steven Spungin 6/10/2020
无需修改几乎完美运行的库。它不是开源的,它是知识共享的,可以免费用于商业用途
0赞 Mike 'Pomax' Kamermans 12/18/2021
如果它是 CC(甚至不是 CC-BY 或 CC-SA),那么不开源就更奇怪了,比普通的 NPM 包需要更多的安全审查。
2赞 therightstuff 6/20/2022 #6

[编辑:此解决方案仅适用于非常简单的对象和数组,但不适用于嵌套对象等更复杂的场景。我建议使用像jsonrepair这样的东西来处理更有趣的情况。

我稍微修改了 Arnaud 的解决方案,以处理键中的句点、键值中的冒号和任意空格(尽管它不处理 JSON 对象键值):

var badJson = `{
    firstKey: "http://fdskljflksf",
    second.Key: true, thirdKey:
    5, fourthKey: "hello"
}`;


/*
    \s*
        any amount of whitespace
    (['"])?
        group 1: optional quotation
    ([a-z0-9A-Z_\.]+)
        group 2: at least one value key character
    (['"])?
        group 3: optional quotation
    \s*
        any amount of whitespace
    :
        a colon literal
    ([^,\}]+)
        group 4: at least one character of the key value (strings assumed to be quoted), ends on the following comma or closing brace
    (,)?
        group 5: optional comma
*/
var correctJson = badJson.replace(/\s*(['"])?([a-z0-9A-Z_\.]+)(['"])?\s*:([^,\}]+)(,)?/g, '"$2": $4$5');
JSON.parse(correctJson);

评论

0赞 mike 2/7/2023
不错,但有数据格式问题。test { _item123456: { data { type: "Object", id: "XY12246", date: "2018-04-12 16:19:42", shape: "cube", state: "painted" } } }
0赞 therightstuff 2/8/2023
是的,此正则表达式不支持嵌套对象。对于这些,我最初的方法是创建一个从外到内的内部对象数组,每个内部对象被一个临时值(例如uuid)替换,然后在每个内部对象上运行上述正则表达式,最后用它们的固定对象替换临时值
0赞 therightstuff 2/9/2023
我已经编辑了我的答案,包括对解决更复杂情况的 npm 包的引用,更安全的 JSON 解析不是一个微不足道的问题。
1赞 ojosilva 4/18/2023 #7

如果您正在编写 NodeJS 代码,您还可以使用该模块来创建比解析宽松 JSON 更安全的环境。虽然环境可以运行任意代码,但会更严密,它是一个纯 V8 沙箱,没有 or 之类的东西。node:vmevalvmrequireprocess

const vm = require('vm'); // or node:vm
const badJson = '{muh: 2}';
try {
    const parsedJson = new vm.Script(`x=${badJson}`).runInNewContext(
        { console: undefined }, // nuke console inside the vm
        { timeout: 1000, displayErrors: true }
    );
    if (typeof parsedJson !== 'object') { // in case you're expecting an object/array
        throw new Error(`Invalid JSON=${badJson}, parsed as: ${parsedJson}`);
    }
    console.log(parsedJson);
} catch (err) {
    throw new Error(`Could not parse JSON: ${err}`);
}

您可以改用一个模块,该模块承诺更高的安全性并执行相同的操作:https://github.com/patriksimek/vm2vm2

评论

0赞 axkibe 4/19/2023
我忘记了很久以前给出的那个问题,但我同意,如果我今天做同样的事情,我会使用 vm 模块。在适当的上下文中。
0赞 Herobrine 5/6/2023 #8

这是我的简单解决方案,适用于值中的嵌套对象和分号:

const finalJson = relaxedJson
  .replace(/([{,])\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*:/g, "$1\"$2\":");