本文比較完整地整理一下 JSON 編碼中的轉義,以及 JSON 對 Unicode 編碼的處理。javascript
其實這是我上一篇文章的姊妹篇。在研究 Unicode 顏文字的時候,因爲咱們的數據傳輸是經過 JSON 串來完成的,在對顏文字進行轉碼傳輸的過程當中,也發現了一個問題。解決問題以後,便有了本總結文。java
我的認爲,JSON 是目前針對程序員而言可讀性(readability)最佳的數據傳輸格式之一,而且 JSON 完整地考慮到了數據傳輸中的轉義,避免出現各類注入風險。當對 JSON 進行序列化操做時(Go 中稱爲 marshal
),根據 JSON 標準的說明,須要對字符串中的如下字符進行轉義:程序員
符號 | 名稱 | 轉義後的字符串 |
---|---|---|
" |
雙引號 | \" |
/ |
斜槓 | \/ |
\ |
反斜槓 | \\ |
\b |
退格符 | \b |
\f |
垂直製表符 | \f |
Tab | 水平製表符 | \t |
\r |
回車 | \r |
\n |
換行符 | \n |
< |
左尖括號 | \u003C |
> |
右尖括號 | \u003E |
& |
And 符號 | \u0026 |
另外針對 Go 語言,我的建議再轉義一個百分號 %
爲 \u0025
,緣由是在 Go 的各類字符串格式化操做中,百分號是一個關鍵字符,這樣能夠避免在打日誌或者其餘設計格式化的操做時出現錯誤。編程
這裏所說的 Unicode 字符,準確而言指的是在 ASCII 範圍以外的字符,也就是值大於 0x7F 的 Unicode 字符。json
其實大部分狀況下,UTF-8 已經成爲現代編程語言約定俗成的標準了,所以在 JSON 序列化時,只要簡單地對 Unicode 字符的值轉爲二進制而後按照網絡字節序打包就能夠了。segmentfault
可是在某些狀況下,當對端採用的不是 UTF-8,或者是對端採用的不是網絡字節序時(好比對方是技術底下/落後、但卻話語權強大的客戶/合做商/集成商),這個時候,你們統一採用 ASCII 編碼,就可以避免這些問題了。網絡
那麼 JSON 是怎麼使用 ASCII 編碼來傳輸 Unicode 的呢?從前文的轉義其實就能夠一窺端倪了——JSON 採用的是 \uXXXX
的形式來表示一個 Unicode 字符的。每一個 Unicode 字符表示法中,XXXX
必須是4個十六進制數,即使高位爲0也須要補全。經過這種方式,編碼和傳輸 Unicode 字符。在 ASCII 爲主的數據傳輸中,這種編碼方式比較穩妥,而且不會額外增長過多的數據量。固然對於 Unicode 字符比較多的狀況下(好比大量的中文),這就須要程序員考慮一下額外帶來的網絡花銷了。編程語言
讀者可能會注意到了,\uXXXX
格式最大隻能支持到 0xFFFF,但 Unicode 早就已經超過了這個範圍。大於 65535 的字符要怎麼表示呢?首先,絕對不是簡單地採用 uXXXXX,這會致使編碼錯誤。編碼
針對大於 65535 的字符,JSON 採用的是 UTF-16 編碼。UTF-16 採用了 Unicode 的一個特性:不超過20位。
好比咱們用 u
表明這樣的一個字符,UTF-16 的處理方法以下:spa
u = u - 0x10000
hi
等於作了減法以後的 u 的高10位:hi = (u & 0xFFC00) >> 10
lo
等於作了減法以後的 u 的低10位:lo = u & 0x003FF
0xD800
後進行 \u
編碼0xDC00
後進行 \u
編碼舉例說明:表明地球的顏文字符號 「🌍」,其編碼值爲 0x1F30D
,按照 UTF-16 編碼過程爲:
u = 0x1F30D - 0x10000 = 0xF30D
,二進制爲:1111 0011 0000 1101
0000111100
,低10位等於 1100001101
0x03C
作加法以後等於 0xD83C
0x30D
作加法以後等於 0xDF0D
\uD83C\uDF0D
好比如下的 JSON:
{ "string":"我是地球🌍" }
按照 ASCII 序列化以後,結果爲:
{"string":"\u6211\u662F\u5730\u7403\uD83C\uDF0D"}
本文章採用 知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議 進行許可。
原做者: amc,歡迎轉載,但請註明出處。
原文標題:JSON 序列化中的轉義和 Unicode 編碼
發佈日期:2020-05-31
本文連接:http://www.javashuo.com/article/p-snrvsutk-mz.html
本文最先發佈於:https://cloud.tencent.com/developer/article/1336510,也是本人的博客