JSON和转义字符

63

我有一个字符串在Javascript中被序列化为JSON,然后在Java中被反序列化。

如果该字符串包含度数符号,则会出现问题。

我需要帮助找出问题的原因:

  • 是Spidermonkey 1.8实现的问题吗?(它内置了一个JSON实现)
  • Google gson的问题吗?
  • 还是因为我没有正确操作?

以下是在JSDB中发生的情况:

js>s='15\u00f8C'
15°C
js>JSON.stringify(s)
"15°C"

我本来期望的是 "15\u00f8C' ,这使我认为Spidermonkey的JSON实现并没有做正确的事情... 除非JSON主页的语法描述(那是规范吗?)说字符可以是任何Unicode字符,除了“-"或"\”或控制字符。所以也许它会将字符串原样传递而不将其编码为\u00f8...在这种情况下,我认为问题出在gson库上。

有人能帮忙吗?

我想我的解决方法是使用其他的JSON库,或者在调用JSON.stringify()之后手动转义字符串--但如果这是一个bug,我想报告一下。


4
咆哮...先不要管我的Windows电脑决定使用字符集,将度符号映射为\u00f8而不是\u00b0的事实! - Jason S
3个回答

88

这不是任何实现中的错误。没有必要转义U+00B0。引用RFC的话:

2.5. 字符串

字符串的表示类似于C系列编程语言中使用的约定。字符串以引号开始和结束。所有Unicode字符都可以放置在引号内,除了必须转义的字符:引号、反斜杠和控制字符(U + 0000到U + 001F)。

任何字符都可以转义。

转义所有字符会增加数据的大小(在所有Unicode转换格式中,所有代码点都可以用四个或更少的字节表示;而将它们全部编码则使它们变成六个或十二个字节)。

更有可能的是,在您的代码中存在文本转码错误,而对ASCII子集中的所有内容进行转义掩盖了该问题。 JSON规范要求所有数据使用Unicode编码。


14
@user: 在支持英语基本字母之外的字符上并没有任何懒惰。欢迎来到2015年,我们不再使用ASCII码了。 - T.J. Crowder

79

嗯,好吧,无论如何这里有一个解决方法:

function JSON_stringify(s, emit_unicode)
{
   var json = JSON.stringify(s);
   return emit_unicode ? json : json.replace(/[\u007f-\uffff]/g,
      function(c) { 
        return '\\u'+('0000'+c.charCodeAt(0).toString(16)).slice(-4);
      }
   );
}

测试用例:

js>s='15\u00f8C 3\u0111';
15°C 3◄
js>JSON_stringify(s, true)
"15°C 3◄"
js>JSON_stringify(s, false)
"15\u00f8C 3\u0111"

3
这里重新提及一篇旧回答,但似乎对于 BMP 以外的字符,这种方法将无法生效。 - owacoder
修复它就是简单地将边界的上限增加到"\u10ffff"吗? - CodeThief
@owacoder,它偶然在BMP之外工作,因为正则表达式匹配不是Unicode感知的pre-ES2015,并且如果您将/g更改为/gu,它会在符合ES2015的引擎上中断。值得庆幸的是,出于向后兼容性的原因,默认的/g行为保持不变,因此它仍然可以在2023年工作。 - wizzard0

9

这个回答来得太晚了,可能已经不相关了,但如果有人发现这个答案,我认为我知道原因所在。

正如其他答案提到的那样,带度符号的JSON编码字符串是完全有效的。问题很可能出在你读写字符编码上。根据你使用Gson的方式,你可能正在传递一个java.io.Reader实例。每当你从InputStream创建一个Reader时,你必须指定字符编码或java.nio.charset.Charset实例(通常最好使用java.nio.charset.StandardCharsets.UTF_8)。如果你没有指定Charset,Java将使用你的平台默认编码,在Windows上通常是CP-1252


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