JSON.stringify是否应该转义Unicode字符?

17

我有一个简单的UTF-8测试页面,其中包含多种不同语言的字母文本,这些文本被字符串化为JSON:

http://jsfiddle.net/Mhgy5/

HTML:

<textarea id="txt">
検索 • Busca • Sök • 搜尋 • Tìm kiếm • Пошук • Cerca • Søk • Haku • Hledání • Keresés • 찾기 • Cari • Ara • جستجو • Căutare • بحث • Hľadať • Søg • Serĉu • Претрага • Paieška • Poišči • Cari • חיפוש • Търсене • Іздеу • Bilatu • Suk • Bilnga • Traži • खोजें
</textarea>
<button id="encode">Encode</button>
<pre id="out">
</pre>

JavaScript:

​$("#encode").click(function () {
    $("#out").text(JSON.stringify({ txt: $("#txt").val() }));
}).click();
​

我预期根据JSON规范,非ASCII字符会被转义为\uXXXX,但它们似乎没有被转义。这是上面测试的输出结果:
{"txt":"検索 • Busca • Sök • 搜尋 • Tìm kiếm • Пошук • Cerca • Søk • Haku • Hledání • Keresés • 찾기 • Cari • Ara • جستجو • Căutare • بحث • Hľadať • Søg • Serĉu • Претрага • Paieška • Poišči • Cari • חיפוש • Търсене • Іздеу • Bilatu • Suk • Bilnga • Traži • खोजें\n"}
我使用的是Chrome浏览器,所以应该是原生的JSON.stringify实现。页面的编码方式是UTF-8。难道非ASCII字符不应该被转义吗?
首先让我来说一下为什么我要进行这个测试,我发现jQuery.ajax在数据对象属性中出现非ASCII字符时似乎没有对它们进行转义,这些字符似乎以UTF-8形式传输。

1
我认为你的断言,即每个非ASCII字符都必须转换为转义序列,是不准确的,甚至离真相很远。 - Kerrek SB
2
可能是JSON和转义字符的重复问题。 - James Montagne
5个回答

38

JSON规范并不要求将Unicode字符转换为转义序列。定义任何“除了 " 或 \ 或控制字符以外的UNICODE字符”都是有效的JSON序列化字符串:

json string format


3
仅仅因为规范没有要求并不意味着它不值得实现。事实上,\uXXXX 格式就在底部,并且经常需要与不支持超过 7 位表示的外部服务和/或传输进行互操作。JS 的本地 JSON 编码器基本上无法生成符合其自身规范的输出,这是可笑的,而对此的各种解决方法本身经常成为其各自堆栈中更深层次问题的贡献者。 - Sammitch

11

实际上,JSON.stringify不会转义UTF-8字符:

JSON.stringify({a:"Привет!"})
{"a":"Привет!"}

但是,当我使用Perl DBD::Mysql存储JSON并重新检索它时出现了问题。我发现按照建议通过\uXXXX转义所有非ASCII和非可见字符更加安全,以下是如何实现的。

function jsonEscapeUTF(s) {return s.replace(/[^\x20-\x7F]/g, x => "\\u" + ("000"+x.codePointAt(0).toString(16)).slice(-4))}

jsonEscapeUTF(JSON.stringify({a:"Привет!"}))
"{"a":"\u041f\u0440\u0438\u0432\u0435\u0442!"}"

希望对您有所帮助。


请注意,这只是偶然有效的,因为默认情况下正则表达式不进行 Unicode 感知匹配(例如 "".codePointAt(0) 是 128190,因此 /[^\x20-\x7F]/gu 正则表达式不再适用于表情符号)。 - wizzard0

5

对于你的问题,简短的回答是不应该;JSON.stringify 不会转义你的字符串。

但是,如果你使用 utf-8 编码保存 HTML 文件却没有声明为 utf8 文件,处理 utf8 字符串可能看起来很奇怪。

例如:

<!doctype html>
<html>
    <head>
        <title></title>
        <script>
            var data="árvíztűrő tükörfúrógép ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP";
            alert(JSON.stringify(data));
        </script>
    </head>
</html>

这将会弹出警告框:"árvíztűrÅ‘ tükörfúrógép ÃRVÃZTÅ°RÅ TÃœKÖRFÚRÓGÉP"

但是如果您在头部添加以下行:

<meta charset="UTF-8">

接下来,警报将会是我们所期望的:"árvíztűrő tükörfúrógép ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP"


3

不需要。JSON的首选编码是UTF-8,因此这些字符不需要转义。

如果您想更安全或显式地以不同的编码(即纯ASCII)发送JSON,则可以转义Unicode字符,但这违反了建议。


1

你的说法并不正确。JSON字符串由Unicode码点组成(除了“"”和“\”),仅此而已。整个JSON文档可以使用UTF-8、UTF-16或UTF-32进行编码,由生产者自行决定。此外,字符串可以包含转义序列,提供一种替代形式来命名码点,而不是直接包含它们。

如果你还是分不清两者之间的区别,这里有一个在JSON中用两种不同方式写同一个字符串的例子:

  • "A"

  • "\u0041"

这两个版本都表示相同的字符串,由单个码点U+41组成,即A


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