对于JSON的编码和解码已经足够方便,几乎在各种编程语言之中都已经实现。在PHP之中,有json_encode 和 json_decode 函数实现。
但是有一个问题,json_encode 在编码时,会把中文字符编码为Unicode字符。
1 2 3
| $arr = array('test'=>'测试'); $str = json_encode($arr); echo $str;
|
结果:
因此,先用urlencode编码字符串值,再json_encode编码成JSON,最后用urldecode解码。
1 2 3 4 5 6 7 8 9
| function string_urlencode(&$value){ if(is_string($value)){ $value = urlencode($value); } } $arr = array('test'=>'测试'); array_walk_recursive($arr, 'string_urlencode'); $str = json_encode($arr); echo urldecode($str);
|
结果:
那么问题来了,如果key/value 中的值有特殊字符,比如双引号,回车符,换行符等的时候,用上面这种方法转换出的字符串就不能正常解析为JSON字符串。
解决办法一:
1 2 3 4 5 6 7 8 9 10
| function string_urlencode(&$value){ if(is_string($value)){ $value = mysql_escape_string($value); $value = urlencode($value); } } $arr = array('test'=>'测"试'.PHP_EOL.'换行'); array_walk_recursive($arr, 'string_urlencode'); $str = json_encode($arr); echo urldecode($str);
|
结果:
这是我首先想到的解决办法。但是mysql_escape_string是MySQL的专用函数,不可靠,所以又想换成addslashes函数,却没有成功
1 2 3 4 5 6 7 8 9 10
| function string_urlencode(&$value){ if(is_string($value)){ $value = addslashes($value); $value = urlencode($value); } } $arr = array('test'=>'测"试'.PHP_EOL.'换行'); array_walk_recursive($arr, 'string_urlencode'); $str = json_encode($arr); echo urldecode($str);
|
于是查了下php手册,addslashes只能转义 ’ " \ null 四种字符
mysql_escape_string 转义的字符会更多,多了\r \n 和另一个Control-Z 字符,别问我Control-Z是什么,你可以参考这里
so, 上面的代码改一下
解决办法二:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function string_urlencode(&$value){ if(is_string($value)){ $value = addslashes($value); $value = str_replace( array(chr(13),chr(10)), array('\r','\n'), $value ); $value = urlencode($value); } } $arr = array('test'=>'测"试'.PHP_EOL.'换\行'); array_walk_recursive($arr, 'string_urlencode'); $str = json_encode($arr); echo urldecode($str);
|
结果:
当然,因为JSON的规则是确定的,并且对于哪些字符需要转义也是确定的。
so,我们可以更极端一点,干脆不用addslashes函数,自己把所有的特殊字符转义了
解决办法三:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function string_urlencode(&$value){ if(is_string($value)){ $value = str_replace( array('\\','"','/',chr(8),chr(12),chr(13),chr(10),chr(9)), array('\\\\','\"','\/','\b','\f','\r','\n','\t'), $value ); $value = urlencode($value); } } $arr = array( 'test'=>'测"试'.PHP_EOL.'换'.chr(8).'行'.chr(12).'制'.chr(9).'表'.'\\ha' ); array_walk_recursive($arr, 'string_urlencode'); $str = json_encode($arr); echo urldecode($str);
|
结果:
1
| {"test":"测\"试\r\n换\b行\f制\t表\\ha"}
|
解决办法四:
php5.4版本开始已经提供了原生的解决办法
1 2 3 4 5
| $arr = array( 'test'=>'测"试'.PHP_EOL.'换'.chr(8).'行'.chr(12).'制'.chr(9).'表'.'\\ha' ); $str = json_encode($arr, JSON_UNESCAPED_UNICODE); echo $str;
|
1
| {"test":"测\"试\r\n换\b行\f制\t表\\ha"}
|
总结分析:
办法一: 方便实用,但是 mysql_escape_string 函数在未来php版本可能被移除(后记:在php7中已经移除)
办法二: 比较简单,够用
办法三: 强迫症+深究型比较喜欢的方式,很够显比格?
办法四: 部署环境必须要PHP的版本在5.4及以上
但是客观的说,如果环境允许,选择第四种,其次二三择其一,最次选择第一种。
End–
文章标题:如何在 JSON 字符串中不转码 Unicode 字符