当一个字符串值包含逗号时,JSON.parse在Safari中失败

14

我正在构建一个购物车,当按下“购买”按钮时,一个web服务返回一个JSON输出,我将其保存为一个Javascript cookie字符串。

Web服务的典型返回值可能是:

{
   "d":{
      "58658":{
         "id":"58658",
         "qty":"1",
         "singlePrice":"754",
         "salePrice":"754",
         "isBulk":"0",
         "isSor":"0",
         "vatRate":"20.00",
         "masterCat":"6",
         "imgUrl":"http://...img url",
         "singleWeight":"18000",
         "totalAvailableStock":"2",
         "thirdPartyStock":"",
         "item":"Electrovoice Live X Elx115p, Each " // << HERE IS THE ISSUE
      }       
   }
}

当返回值如上所示时,仅在Safari中会失败。

花费了很多时间寻找一些不用购买Mac就能调试的有用机制,最终我找到了解决方法:

"item":"Electrovoice Live X Elx115p, Each " // << HERE IS THE ISSUE - The comma

item的值在字符串中有逗号,我会使用类似以下方式将返回值保存到cookie中:

cookies.set('shopping_cart', JSON.stringify(result)); // (where result is the JSON above)

当我稍后尝试回忆它时:

var shopping_cart = cookies.get('shopping_cart');
shopping_cart = JSON.parse(shopping_cart);

它在所有浏览器中都能正常工作,甚至包括Internet Explorer...但Safari除外。

我没有Mac电脑,所以不确定Mac上的Safari是否有所不同,但Windows版本肯定存在此错误,并且我的iPhone和iPad上也是如此。

这个错误是:

JSON parse error, unterminated string.

正如我所说,这似乎只是在Safari中存在的问题,但我很难找到解决方案。
非常感谢您的帮助!
编辑:
我创建了一个Fiddle:http://jsfiddle.net/jhartnoll/2GLEz/ 这个Fiddle将复制此问题,它涉及将数据存储为Cookie中的字符串并将其重新解析为JSON.
但是,因为我将我的Cookie函数作为外部资源(http://www.sam.thinkka.com/clientscripts/cookie_test.js)包含在内,您需要确保Safari未阻止第三方Cookie。

不,逗号不是问题。 - Akshay Khandelwal
你能提供一个 jsFiddle 的示例来演示这个问题吗? - Spudley
好的,为了让这个小工具正常运行,你必须停止 Safari 阻止第三方脚本的功能。但是你可以在这里找到它:http://jsfiddle.net/jhartnoll/2GLEz/ - Jamie Hartnoll
嗯...我认为问题与JSON或我的Cookies无关,而是https://discussions.apple.com/thread/4078916?start=0&tstart=0。 - Jamie Hartnoll
1
好的,我认为现在我们已经有了你的fiddle代码,我们正在接近问题的核心。基本上,它归结为转义。但请参阅我的答案以获取更多信息。 - Spudley
显示剩余3条评论
2个回答

15

好的,现在我们已经找到了实际问题的根源,我想我能帮忙。

问题基本上在于您将数据存储到cookie字符串中,但没有对其进行转义。因此,当您设置cookie并且它包含逗号时,逗号可以被视为cookie分隔符。对于大多数字符串,JSON中的引号似乎掩盖了此效果(引号也是cookie的相关字符),但是对于这个字符串,cookie解析器将其视为定界符。

这反过来意味着它被视为cookie的结尾,这意味着当您重新加载它时,它会在该点截断。

解决方案:在保存为cookie之前转义字符串。(您的makeCookie()函数应该做到这一点)。您应该能够使用JS的escape()函数来完成。

请参见cookie规范,请注意,逗号、双引号、分号等都是cookie的相关字符。

同时请参见:这个类似的问题

至于为什么它会在Safari中发生而其他浏览器没有...我猜这可能与运气有关。也许其他浏览器的cookie解析器认识到逗号后面的字符串无效,因此确定逗号不是作为分隔符意图的。

您还可以参见MDN cookie文章,其中包括用于示例JS coookie库的代码,该库确实执行了所有正确的转义。您可能要考虑使用此库而不是当前的代码。(或者至少使用其中的某些部分)

希望对您有所帮助。

最后,略微偏离主题,但在使用cookie时还需要注意的一件事:不要忘记每次在您的站点上进行http请求时,整个cookie字符串都会在两个方向上传输。因此,如果您有4k的cookie数据,则对于站点上的每个html、css、js和图像文件,额外的8k带宽就会被消耗。

如果您正在使用大型cookie,有替代方法可以存储本地数据,而不会受到这个问题的影响。该文章可能对您有所帮助。


1
太好了。谢谢...现在进行了两个很好的修改,使用localStorage似乎更有意义,我已经修改了我的Cookies函数,尽可能使用localStorage,如果不行就回滚到Cookies。 - Jamie Hartnoll
我的 cookies 回退中添加了转义。 - Jamie Hartnoll

0

你不需要解析这个 JSON... 尝试一下:

var a= {
    "d":{
        "58658":{
            "id":"58658",
            "qty":"1",
            "singlePrice":"754",
            "salePrice":"754",
            "isBulk":"0",
            "isSor":"0",
            "vatRate":"20.00",
            "masterCat":"6",
            "imgUrl":"http://...img url",
            "singleWeight":"18000",
            "totalAvailableStock":"2",
            "thirdPartyStock":"",
            "item":"Electrovoice Live X Elx115p, Each " // << HERE IS THE ISSUE
        }
    }
}

console.log(a.d['58658'])

我知道我不能直接使用它,但如果我想将其保存在Cookie中,我会这样做吗? - Jamie Hartnoll
如果您发布一个fiddle就太好了。 - Vicky Gonsalves
1
已发布Fiddle - 为了让fiddle正常工作,您必须停止Safari阻止第三方脚本,但它在这里:http://jsfiddle.net/jhartnoll/2GLEz/ - Jamie Hartnoll

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