在JSON结果中定义函数是否有效?

103

从网站的JSON响应中,这部分内容是这样的(...是为了方便上下文而添加的):

{..., now:function(){return(new Date).getTime()}, ...}

将匿名函数添加到JSON中是否有效? 我期望每次访问'time'时返回不同的值。


浏览器成功解析了JSON吗?如果是,则它是有效的(在这方面)。 - harschware
7
@harschware - 这只对JSON与JavaScript相关的情况为真。作为一种语言无关的数据序列化格式,这是错误的,并且是一条棘手的路要走下去。 - jsoverson
@jsoverson - 我同意。请看下面我的回答。 - harschware
1
很容易自己回答这个问题:打开 WebKit 检查器并运行:JSON.parse('{now:function(){return(new Date).getTime()}')。检查器会显示:Uncaught SyntaxError: Unexpected token n。 快速查看 [JSON 规范](http://www.json.org/)可以确认这一点。重点关注“value”部分。 - Mark E. Haase
11个回答

109

不。

JSON是一种纯粹用于数据描述的语言。正如http://www.json.org所指出的,它是一个“轻量级数据交换格式”,而非编程语言。

根据http://en.wikipedia.org/wiki/JSON,支持的“基本类型”包括:

  • 数值(整型、实型或浮点型)
  • 字符串(双引号Unicode格式,使用反斜杠转义)
  • 布尔值(true和false)
  • 数组(有序列的值,逗号分隔并用方括号括起来)
  • 对象(键值对的集合,逗号分隔并用花括号括起来)
  • null

2
@Dr. Zim,不要将事物与null进行比较,我会这样做a==null?1:a.toString()==""。这样做的意思是,如果a=null,则返回1/true,如果它是"",也就是空字符串,你也会得到1/true。如果它既不是null也不是"",那么它将返回0/false,你可以通过在我的上一个片段中添加?1:a==[]?1:a.toString()=={}.toString();来复制更多内容以处理[]和{}。所以也许这个函数会帮助你。isnull=(function(a){return (a==null?1:a.toString()==""?1:a==[]?1:a.toString()=={}.toString())?true:false})我会使用?1:0而不是?true:false,但(true/false)。 - JamesM-SiteGen
14
同时,函数也是数据。 - argyle
2
我在寻找使用JSON获取“进一步数据”的方法时来到了这里。服务器端向客户端提供如何获取进一步数据的信息是很不错的,这样客户端就不必担心需要调用哪个REST或其他API。 - Ravindranath Akila
3
REST 的含义是暴露在调用中可能出现的下一个 API 调用。换句话说,你所做的 REST 请求告诉你基于应用决策逻辑和数据可能需要进行的未来请求。Github API 是一个完美的示例,其中返回数据元素,但其中一些会导致其他 API 请求资源。 - Jens A. Koch
1
@Jens-AndréKoch 谢谢!我会查看的。 - Ravindranath Akila
显示剩余3条评论

16
问题在于,JSON作为数据定义语言是从JavaScript对象表示法(JSON)演变而来的。由于JavaScript支持对JSON进行eval操作,在该用例中将JSON代码放入JSON中是合法的。如果您使用JSON远程传递数据,则我会说将方法放入JSON是不好的实践,因为您可能没有很好地建模客户端-服务器交互。此外,当希望将JSON用作数据描述语言时,嵌入方法可能会导致麻烦,因为一些JSON解析器只考虑数据描述,并且可能不支持结构中的方法定义。 维基百科JSON条目提出了不包括方法在JSON中的充分理由,引用了安全方面的考虑:
“除非您绝对信任文本的来源,并且需要解析和接受不严格符合JSON规范的文本,否则应避免使用eval(),而应使用JSON.parse()或其他JSON特定解析器。 JSON解析器仅识别JSON文本并将拒绝其他文本,其中可能包含恶意JavaScript。在提供本机JSON支持的浏览器中,JSON解析器也比eval快得多。预计本机JSON支持将包含在下一个ECMAScript标准中。”

2
你可能在口语中使用JSON这个术语,但官方上来说,“JSON”是一个ECMA标准,不允许编码功能对象。当你说“JSON”时,应该没有任何歧义,指的是哪些能力——这就是制定标准的整个目的所在。 - Mark E. Haase
同意这是今天的真相。我没有引用的来源,但我相信在ECMA进入Javascript游戏之前,JSON就是一个被创造出来的术语,并且在JSON成为标准数据交换格式之前...因此我使用了“演变”这个词。 - harschware
根据RFC 4627,JSON是由ECMA制定的。 - Jenna Sloan

11

让我们引用规范中的一段 - https://www.rfc-editor.org/rfc/rfc7159#section-12

JavaScript对象表示法(JSON)数据交换格式规范 表示:

JSON是JavaScript的一个子集,但排除了赋值和调用。

由于JSON的语法借鉴了JavaScript,因此可以使用该语言的“eval()”函数解析JSON文本。这通常构成了一个不可接受的安全风险,因为文本可能包含可执行代码以及数据声明。相同的考虑也适用于在任何其他编程语言中使用类似eval()的函数,其中JSON文本符合该语言的语法。

因此,所有声称函数不是JSON标准一部分的答案都是正确的。

官方答案是:不,定义JSON结果中的函数无效!


答案可能是肯定的,因为“代码是数据”和“数据就是代码”。即使将JSON用作独立于语言的数据序列化格式,通过其他类型的“隧道”传递“代码”也可以工作。

JSON字符串可能被用来传递JS函数给客户端浏览器执行。

[{"data":[["1","2"],["3","4"]],"aFunction":"function(){return \"foo bar\";}"}]

这导致了类似于以下问题的提出:如何执行存储为字符串的JavaScript代码。

准备好,升起您的“eval()是邪恶的”旗帜,并将您的“不要通过JSON隧道函数”旗帜贴在旁边。


8
据我所知,这不是标准规范。快速查看http://json.org/可以证实这一点。

5

不是的,绝对不是这样。

如果你使用一个良好的JSON序列化程序,它不会让你像那样序列化一个函数。它是一个有效的对象,但不是有效的JSON。无论该网站的意图如何,它都没有发送有效的JSON。


2
我使用JSON-Lib,并认为它是一个很棒的序列化工具。从用法页面上可以看到,它可以很好地序列化函数。 - harschware
有趣...我以前从未见过这样的。它肯定不符合规范(http://www.json.org/ 明确指出JSON是与编程语言无关的,而函数定义则不是),但仍然很有趣。 - Jerod Venema
1
很有趣的是,它应该是语言无关的,但 JSON 代表 JavaScript 对象表示法,嗯,有点奇怪。 - Nate-Wilkins

4
短答案是不行...

JSON是一种文本格式,完全独立于语言,但使用的约定俗成的是C语音家族程序员熟悉的,包括C、C++、C#、Java、JavaScript、Perl、Python等。这些特性使JSON成为一种理想的数据交换语言。

看看为什么:

在浏览器和服务器之间交换数据时,数据只能是文本。

JSON是文本,我们可以将任何JavaScript对象转换为JSON,并将JSON发送到服务器。

我们还可以将从服务器接收到的任何JSON转换为JavaScript对象。

这样,我们就可以使用JavaScript对象处理数据,无需复杂的解析和转换。

但是等等...

仍有方法存储函数,虽然不推荐它,但仍然可能:

我们说过,你可以保存一个string...将你的函数转换为字符串如何?

const data = {func: '()=>"a FUNC"'};

您可以使用 JSON.stringify(data) 将数据转换为字符串,然后使用 JSON.parse 解析它(如果需要的话)...

并且可以使用eval执行字符串函数(在执行之前,请了解使用 eval 并不是一个广泛推荐的做法):

eval(data.func)(); //return "a FUNC"

4

JSON明确排除函数,因为它不仅仅是JavaScript数据结构(尽管名称中有JS)。


0
通过使用NodeJS(commonJS语法),我能够使这种类型的功能正常工作。最初,我只是在一些外部JS文件中有一个JSON结构,但我希望该结构更像一个类,具有可以在运行时决定的方法。
在myJSON中,不需要声明“Executor”。
var myJSON = {
    "Hello": "World",
    "Executor": ""
}

module.exports = {
    init: () => { return { ...myJSON, "Executor": (first, last) => { return first + last } } }
}

-5

在JSON中,函数表达式是完全可行的,只需不要忘记用双引号括起来即可。以下是从noSQL数据库设计中摘取的一个示例:

{
  "_id": "_design/testdb",
  "views": {
    "byName": {
      "map": "function(doc){if(doc.name){emit(doc.name,doc.code)}}"
    }
  }
}

这个问题是关于一个函数而不是一个字符串。JSON支持字符串值,但不支持函数。有关详情,请参见 Mike 的答案 - jannis
@jannis 可以的。如果你添加一个自调用函数,就像这样。这里的人显然不知道答案。 - Rogelio

-6

尽管不建议使用eval,但以下代码可行:

<!DOCTYPE html>
<html>
<body>

<h2>Convert a string written in JSON format, into a JavaScript function.</h2>

<p id="demo"></p>

<script>
    function test(val){return val + " it's OK;}
    var someVar = "yup";
    var myObj = { "func": "test(someVar);" };
    document.getElementById("demo").innerHTML = eval(myObj.func);
</script>

</body>
</html>

1
因为示例中不必要使用HTML,这是5年前的编码风格,缺少引号,而且JSON文件不应包含函数,如果包含函数,则无法序列化或存储在NoSQL数据库中,所以被投票否决。 - Martijn Scheffer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接