1. 程式人生 > >JSON 被轉義的字元引發BUG問題

JSON 被轉義的字元引發BUG問題

結論:

1、  在json_encode時候,第二個引數加上JSON_UNESCAPED_UNICODE。前提是需要PHP 5.4以上版本支援

2、  遇到json資料異常時候,請先關注字元是否包含\u000-\uffff這樣字元,如果有需要想辦法處理,否則json會轉義。

背景:今天在和搜尋部門同事進行搜尋質量調優時候遇到一個問題,就是一個數據生成時候json格式正確。灌入他們引擎,然後在搜尋時候底層請求他們介面有資料,而我們這邊json_decode之後資料為空。

 

簡單理解為:這條資料json_decode失敗,導致出不來資料。進一步排查對比發現某個欄位不是純文字描述,他包含“潛在編碼”,而這個編碼導致json_encode時候出現\u0010 這種被轉義的字元,

 

 

其實,之前我一直以為 JSON 會把 ASCII 可顯示字元以外的統統轉義為 Unicode,
直到有一次我用 JSON.stringify 才發現,其實是 PHP 為我們想的太周到了。

我以前是一位 phper,所以處理 json 只要 json_encode 就可以把陣列轉為 json 資料了,非常方便。

可以看到,預設就是把所有 ASCII 可顯示字元以外的統統轉義為 Unicode。

這樣做有什麼好處呢?
大家在呼叫 jsonp 介面或者呼叫js檔案的時候,由於檔案編碼不同導致的亂碼問題,應該不會陌生吧。
如果你的檔案出現了非英文字元,如果呼叫時檔案編碼不一致,則會出現亂碼情況。
很多新手朋友應該都糾結過這種問題吧。

但是如果把那些字元轉義為 Unicode 之後,無論檔案編碼是否一致,都不會出現亂碼。
這就是為什麼 PHP 會預設編碼為 Unicode 的原因,她為我們想的太周到了。

當然如果你非要直接顯示那些字元,也是OK的,第二個引數加上 JSON_UNESCAPED_UNICODE 即可。
但是這個引數 PHP 5.4.0 才開始支援。

那麼 JSON.stringify 會轉義哪些呢?
在 json2.js 第 351 行可以看到這個正則。

文字
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g
;

也就是說 JSON 只會轉義這部分字元為 unicode,我們來簡單測試下吧。

文字執行
console.log( JSON.stringify("\x00 \x0a") );

點執行後,可以看到 \x00 被轉義為 \u0000 而 \x0a 卻被專為了 \n
像 \n 這些特殊字元的轉換在剛才那個正則下面就可以看到了。

但是你測試字元 \ufeff 的時候會發現 firefox 和 chrome 根本沒轉義。
確實,,好像只有 json2 為我們轉義了。

為什麼原生 JSON.stringify 這麼多字元都沒轉義,難道他就沒為我們考慮相容問題麼?
其實我覺得,這個問題可以不要考慮,因為你不會直用靜態的頁面為其他站點提供介面之類的。
往往只是自己內部用而已,就算提交給後臺,一個專案下編碼也是一樣的,所以內部不需要考慮那些相容問題。
就好比在自己老家,難道你要普通話或英文跟他們交流麼?
直接用方言交流才更加流暢。

當然這個只是我個人觀點,也不知道寫js引擎的大神是怎麼想的。

我們來遍歷下原生 JSON 對 \u000-\uffff 這些字元的轉義情況吧。

文字執行
for (var i = 0, str = '', arr = []; i < 0xffff; i++) {
    str = JSON.stringify(String.fromCharCode(i));
    str.indexOf("\\") > -1 && arr.push(str);
}
console.log(arr.join(", "));

我的 chrome 34 得到的結果是

文字
["\u0000", "\u0001", "\u0002", "\u0003", "\u0004", "\u0005", "\u0006", "\u0007", "\b", "\t", "\n", "\u000b", "\f", "\r", "\u000e", "\u000f", "\u0010", "\u0011", "\u0012", "\u0013", "\u0014", "\u0015", "\u0016", "\u0017", "\u0018", "\u0019", "\u001a", "\u001b", "\u001c", "\u001d", "\u001e", "\u001f", "\"", "\\"];