【问题标题】:Go is generating unescaped control characters in JSON output due to emoji由于表情符号,Go 在 JSON 输出中生成未转义的控制字符
【发布时间】:2016-09-28 20:22:37
【问题描述】:

我在 Go 中遇到了一些问题,我不知道该去哪里找。我正在从 MySQL 数据库中获取 UTF-8 字符串,并尝试以 JSON 响应的形式将其返回给客户端。

不同的客户端反应不同,但 iOS NSJSONSerialization 返回“未转义的控制字符”错误。这破坏了整个应用程序。不过,我可以使用 JSON.parse() 在 Chrome 中毫无问题地解码 JSON。

在服务器端,使用除 Go 之外的另一种语言编写的相同生成器函数可以正常工作。帮忙?


编辑:这是导致问题的 JSON:

{ "test":"☮️" }

...如果我省略这个表情符号,它会起作用。如果它在那里,它就不起作用。这个问题似乎与某些表情符号有两种不同的编码有关。一个似乎绊倒了 Go,但它们都是有效的。

为了演示编码的差异,一些表情符号会显示在数据库资源管理器中,而另一些则不会:

... 出现在数据库资源管理器中的这些问题以 100% 的可重现性导致了此问题。 但是,它们通常都出现在实际的客户端软件(不是数据库浏览器)中没有问题。我不知道是否有办法重新配置数据库连接以避免这种情况(或其他),但它似乎适用于不同的实例,具体取决于解码的内容以及它的宽容程度。考虑到用户可以键入或复制/粘贴任一编码...这需要始终如一地工作。

任何帮助将不胜感激。提前致谢。

【问题讨论】:

  • 包含导致错误的 JSON 将非常有帮助。
  • 已发布。这是表情符号之一。没有它,它工作正常。
  • JSON 中test 的值为\u262e\ufe0f。第一个字符是PEACE SYMBOL;第二个是VARIATION SELECTOR-16。我怀疑后者的存在与否是导致表情符号显示或不显示的原因,以及错误。请确认。
  • 我只尝试过完全使用和不使用表情符号,但我猜你可能在第二个字符上是对的。尽管如此,我遇到的问题是我无法控制用户输入,但出于某种原因,Golang 对此的翻译被客户端认为是无效的,但 PHP 中的相同脚本返回正常,没有任何处理问题。 (从同一个数据库中获取)
  • 它对我来说很好用。也许“数据库浏览器”坏了?

标签: json go unicode emoji utf8mb4


【解决方案1】:

Go 运行良好。

fmt.Println([]byte("☮️"))
//[226 152 174 239 184 143]
//Yup, 1 character - 6 bytes.

NSJSONSerialization 无法处理这个问题。可能这个链接会有所帮助 NSJSONSerialization and Emoji。这与NSData * utf32Data = [uniText dataUsingEncoding:NSUTF32LittleEndianStringEncoding]; 有关。废话

你能给我们“iOS 风格”中“☮️”符号的字节表示吗,就像我对 go 所做的那样?

UPD

我做了一些研究,您的数据库编码似乎有问题。是UTF16吗?

看看这个

// it look the same, but completely different "characters"
//first one is yours, and second one is U+262E
const nihongo = "☮️☮"
for index, runeValue := range nihongo {
        fmt.Printf("%#U starts at byte position %d\n", runeValue, index)
}
bad := []byte("☮️")
good := []byte("☮")
fmt.Printf("%v %s \n", bad, bad)
fmt.Printf("%v %s \n", good, good)

输出:

U+262E '☮' starts at byte position 0
U+FE0F '️' starts at byte position 3
U+262E '☮' starts at byte position 6
[226 152 174 239 184 143] ☮️ 
[226 152 174] ☮ 

UDP2

它刚刚击中了我!我一直在用你的符号做 ctrl+c/ctrl+v 。但它不是一个单一的符号!它的 2 个符号和第二个是不可打印的。

unprintable := []byte{239, 184, 143}
fmt.Printf("valid? %v", utf8.Valid(unprintable))
fmt.Println("full rune?", utf8.FullRune(unprintable))
r, size := utf8.DecodeRune(unprintable)
fmt.Println(r, size, string(r))
fmt.Printf("valid rune? #v", utf8.ValidRune(r))

输出:

valid? true
full rune? true
65039 3 ️
valid rune? true

所以,你的数据库很好,不可打印的“字符”很好,但是 NSJSONSerialization 不能处理它。最好问问 iOS 社区 =)

【讨论】:

  • “字符”在这种情况下是一个有点模糊的术语;这六个字节是组成一个可见符号的两个代码点(Go 语言中的两个符文)。
  • 好的,但是将这一切归咎于 iOS 是不公平的,因为如果我在 PHP 中使用相同的数据库和相同的 iOS 客户端编写相同的脚本,它就不存在这个问题。这是 Golang 的东西,因为那是不同的组件。
  • 另外,澄清一下,数据库中的存储格式是utf8mb4。如前所述,它通过 PHP 中的脚本返回表情符号,但不是 Golang。 (我更喜欢 Golang 版本,所以我希望我能解决这个问题,因为我无法控制可能是这些变化中的任何一种的用户输入。)
  • 我给了你关于 go 如何处理这个字符的信息,你给了我“它适用于 PHP”的理由。公平吗?给我们 PHP 从数据库中读取的内容(PHP 可以在内部清理字符串),尝试用 PHP 对其进行编码并显示输出。尝试使用 NSJSONSerialization 解码 ☮ ([226 152 174]) 1 个字符,尝试在 iOS 中对相同的字符进行编码,在 iOS 中尝试对不可打印的字符 ([239, 184, 143]) 进行编码。我想你明白了。
  • 对此进行了认真的挖掘,并在客户端某处发现了一个非常奇怪的编码错误。所以你是对的,这不是围棋问题!事实上,Go 实际上做的一切都是正确的,这一事实才让人们明白了这一点。 :)
猜你喜欢
  • 2015-12-11
  • 2014-05-31
  • 2023-03-22
  • 2013-10-04
  • 1970-01-01
  • 2022-01-05
  • 2012-06-26
  • 2018-04-04
  • 2017-08-17
相关资源
最近更新 更多