JSON.parse和JSON.stringify不是幂等的,而且很糟糕

时间:2015-06-03 19:56:24

标签: json node.js

这个问题是多部分的 -

(1a) JSON是JavaScript的基础,为什么没有JSON类型? JSON类型是一个格式为JSON的字符串。在数据被更改之前,它将被标记为已解析/字符串化。一旦数据被更改,它就不会被标记为JSON,并且需要重新解析/重新字符串化。

(1b)在某些软件系统中,是否可能(意外地)尝试通过网络而不是序列化JS对象发送普通的JS对象?为什么不试图避免这种情况?

(1c)为什么我们不能在一个直接的JavaScript对象上调用JSON.parse而不先将其字符串化?

    var json = {   //JS object in properJSON format
        "baz":{
            "1":1,
            "2":true,
            "3":{}
        }
    };

    var json0 = JSON.parse(json); //will throw a parse error...bad...it should not throw an error if json var is actually proper JSON.

所以我们别无选择,只能这样做:

 var json0= JSON.parse(JSON.stringify(json));

但是,存在一些不一致之处,例如:

JSON.parse(true); //works
JSON.parse(null); //works
JSON.parse({}); //throws error

(2)如果我们继续在同一个对象上调用JSON.parse,最终会抛出错误。例如:

var json = {   //same object as above
    "baz":{
        "1":1,
        "2":true,
        "3":{}
    }
};

var json1 = JSON.parse(JSON.stringify(json));
var json2 = JSON.parse(json1); //throws an error...why

(3)为什么JSON.stringify无限地向输入添加越来越多的斜杠?它不仅难以读取调试结果,而且它实际上会使您处于危险状态,因为一个JSON.parse调用不会返回一个普通的JS对象,您必须多次调用JSON.parse才能返回普通的JS对象。这很糟糕,这意味着在给定的JS对象上多次调用JSON.stringify是非常危险的。

   var json = {
        "baz":{
            "1":1,
            "2":true,
            "3":{}
        }
    };

    var json2 = JSON.stringify(json);
    console.log(json2);

    var json3 = JSON.stringify(json2);
    console.log(json3);

    var json4 = JSON.stringify(json3);
    console.log(json4);

    var json5 = JSON.stringify(json4);
    console.log(json5); 

(4)我们应该能够在不改变结果的情况下反复调用的函数的名称是什么(IMO JSON.parseJSON.stringify应该如何表现)?正如你在评论中看到的那样,最好的术语似乎是“幂等的”。

(5)考虑到JSON是一种可用于网络对象的序列化格式,您无法拨打JSON.parseJSON.stringify两次或在某些情况下甚至一次也不会产生一些问题。为什么会这样?

如果您是发明Java,JavaScript或其他任何语言的下一个序列化格式的人,请考虑此问题。

IMO对于给定对象应该有两种状态。序列化状态和反序列化状态。在具有更强类型系统的软件语言中,这通常不是问题。但是使用JavaScript中的JSON,如果在同一个对象上调用两次JSON.parse,我们会遇到致命的异常。同样,如果我们在同一个对象上调用两次JSON.stringify,我们就会进入一个不可恢复的状态。就像我说的那样,应该只有两个状态和两个状态,简单的JS对象和序列化的JS对象。

3 个答案:

答案 0 :(得分:3)

1)JSON.parse需要一个字符串,你正在为它提供一个Javascript对象。

2)与第一个类似的问题。您将字符串提供给需要对象的函数。

3)Stringfy实际上需要一个字符串,但是你正在为它提供一个String对象。因此,它应用相同的度量来转义引号和斜杠,就像第一个字符串一样。这样语言就可以理解引号,字符串中的其他特殊字符。

4)你可以为此编写自己的函数。

5)因为你试图做一个非法的转换。这与第一个和第二个问题有关。只要输入了正确的对象类型,您就可以根据需要多次调用它。唯一的问题是额外的斜杠,但它实际上是标准。

答案 1 :(得分:2)

  

我们将从您创作的噩梦开始: string 输入和整数输出。
  IJSON.parse(IJSON.stringify("5")); //=> 5

     

内置的 JSON 功能不会让我们失败: string 输入和 string 输出。
  JSON.parse(JSON.stringify("5")); //=> "5"

     

JSON 必须保留原始数据类型

JSON.stringify视为将数据包装在一个框中的函数,并将JSON.parse视为将其从框中取出的函数。

请考虑以下事项:

var a = JSON.stringify;
var b = JSON.parse;

var data = "whatever";

b(a(data)) === data; // true

b(b(a(a(data)))) === data; // true

b(b(b(a(a(a(data)))))) === data; // true

也就是说,如果我们将数据放在3个框中,我们必须从3个框中取出它。正确?

如果我把数据放在2个盒子中并从1中取出,我还没有拿到我的数据,我拿着一个包含我数据的盒子。正确?

b(a(a(data))) === data; // false

似乎对我有理智......

  1. JSON.parse取消装箱您的数据。如果没有装箱,则无法将其取消装箱。 JSON.parse需要一个字符串输入,并且你给它一个JavaScript对象文字

  2. JSON.parse的第一次有效调用将返回一个对象。在此对象输出上再次调用JSON.parse将导致与#1

  3. 相同的失败
  4. 重复拨打JSON.stringify会多次“包装”我们的数据。因此,当然您必须使用重复调用JSON.parse,然后从每个“框”中获取数据

  5. Idempotence

  6. 不,这完全是理智的。你不能给双印章留下三重印记。

    你永远不会犯这样的错误,不是吗?

    var json = IJSON.stringify("hi");
    IJSON.parse(json);
    //=> "hi"
    

    好的,这是幂等的,但

    var json = IJSON.stringify("5");
    IJSON.parse(json);
    //=> 5
    

    UH OH!我们每次给它一个字符串,但第二个例子返回一个整数。输入数据类型已丢失!

    JSON功能会在这里失败吗?

    var json = JSON.stringify("hi");
    JSON.parse(json);
    //=> "hi"
    

    一切都好。那么"5"呢?

    var json = JSON.stringify("5");
    JSON.parse(json));
    //=> "5"
    

    是的,这些类型已经预先设定了! JSON有效,IJSON没有。

  7. 也许是一个更现实的例子:

    好的,所以你有一个繁忙的应用程序,很多开发人员正在研究它。它使 关于基础数据类型的鲁莽假设。假设这是一个聊天应用程序,它会在消息从一个点移动到另一个点时对消息进行多次转换。

    沿途您将拥有:

    1. IJSON.stringify
    2. 数据在网络中移动
    3. IJSON.parse
    4. 另一个IJSON.parse因为谁在乎?这是幂等的,对吗?
    5. String.prototype.toUpperCase - 因为这是格式化选择
    6. 让我们看一下消息

      bob: 'hi'
      // 1) '"hi"', 2) <network>, 3) "hi", 4) "hi", 5) "HI"
      

      鲍勃的留言看起来不错。我们来看看爱丽丝的。

      alice: '5'
      // 1) '5'
      // 2) <network>
      // 3) 5
      // 4) 5
      // 5) Uncaught TypeError: message.toUpperCase is not a function
      

      哦不!服务器崩溃了。你会注意到,IJSON.parse的重复调用在这里失败了。即使你打过一次它也会失败。

      好像你从一开始就注定了......诅咒鲁莽的开发者和粗心的数据处理!

      如果Alice使用的任何输入也是有效的JSON

      ,则会失败
      alice: '{"lol":"pwnd"}'
      // 1) '{"lol":"pwnd"}'
      // 2) <network>
      // 3) {lol:"pwnd"}
      // 4) {lol:"pwnd"}
      // 5) Uncaught TypeError: message.toUpperCase is not a function
      

      好吧,不公平的例子也许吧?你在想,“我不是那么鲁莽,我 不会像这样在用户输入上调用IJSON.stringifyIJSON.parse!“ 没关系。你从根本上打破了原来的JSON 无法再提取类型。

      如果我使用IJSON打包一个字符串,然后取消装箱,谁知道我会得到什么?当然不是你,当然不是开发人员使用鲁莽的功能。

      • “我会收到一个字符串类型吗?”
      • “我会得到一个整数吗?”
      • “也许我会得到一个对象?”
      • “也许我会得到蛋糕。我希望它是蛋糕”

      这是不可能的!

      你处于一个全新的痛苦世界,因为你从一开始就对你的数据类型一直粗心大意。你的类型很重要,所以要小心处理它们。

        

      JSON.stringify需要对象类型,而JSON.parse需要字符串类型

      现在看到了光吗?

答案 2 :(得分:2)

我将尝试给你一个理由,为什么在没有问题的情况下,JSON.parse不能在同一数据上多次调用。

您可能不知道它,但JSON文档必须是对象。

这是一个有效的JSON文档:

"some text"

允许将此文档的表示形式存储在javascript变量中:

var JSONDocumentAsString = '"some text"';

并继续努力:

var JSONdocument = JSON.parse(JSONDocumentAsString);
JSONdocument === 'some text';

这会导致错误,因为此字符串不是JSON文档的表示

JSON.parse(JSONdocument);
// SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data

在这种情况下,怎么可能JSON.parse猜到JSONdocument(是一个字符串)是一个JSON文档而且它本应该不返回它?