JSON 是一种轻量级的数据交换格式,完全语言无关,但是采用了类似于C系列语言的约定,更详细的介绍可以参考: http://json.org
由于将数组/对象 序列化为JSON字符串的时候基本上只支持 UTF-8/ASCII, 而我们现在很多网站出于历史原因或者支持国产的原因,采用了GBK/GB2312编码,这个时候,直接使用json_encode/json_decode的时候就可能会出问题了。
我们从B/S两个方面谈这个问题。
首先从B(Browser)方面来讲,我们使用JSON作为和S(Server)数据交换的格式,无论如何,S返回的JSON字符串都已经是Unicode的了,由于JavaScript内部采用了Unicode 的编码,JavaScript会根据客户端的编码的不同而自动转化编码,JSON的解析也就不成问题了。比如: 返回的数据是
"\u4e2d\u56fd\u4eba"
无论你的页面是GBK,还是UTF-8的,都将可以正确解析为
“中国人"
比较简单的方式是调用Javascript的eval函数:
try{ eval("var ret = " +"\"\u4e2d\u56fd\u4eba\";");} catch(e){}; alert(ret);
再来看看S(Server)端,json_encode/json_decode会假定给定的数据是UTF-8编码的,这儿就有几种思路了:
- 将数据转化为UTF-8编码的,然后再调用json_encode, 或者首先json_decode,然后再转化为GBK编码的。
- 将数据urlencode,这样所有的数据都是ASCII的了,调用json_encode就没有问题了,json_decode之后也需要 urldecode下。
- 自己编写函数将GBK编码直接转换为unicode代码。
其实第一种和第二种都是同样的思路,即将其编码转换为可encode的编码,下面看看上面提到的解决方案的具体代码:
encode之前转换为utf-8,decode之后转回gbk:
function tb_json_encode($value, $options = 0)
{
return json_encode(tb_json_convert_encoding($value, "GBK", "UTF-8"));
}
function tb_json_decode($str, $assoc = false, $depth = 512)
{
return tb_json_convert_encoding(json_decode($str, $assoc), "UTF-8", "GBK");
}
function tb_json_convert_encoding($m, $from, $to)
{
switch(gettype($m)) {
case 'integer':
case 'boolean':
case 'float':
case 'double':
case 'NULL':
return $m;
case 'string':
return mb_convert_encoding($m, $to, $from);
case 'object':
$vars = array_keys(get_object_vars($m));
foreach($vars as $key) {
$m->$key = tb_json_convert_encoding($m->$key, $from ,$to);
}
return $m;
case 'array':
foreach($m as $k => $v) {
$m[tb_json_convert_encoding($k, $from, $to)] = tb_json_convert_encoding($v, $from, $to);
}
return $m;
default:
}
return $m;
}
encode之前urlencode,decode之后urldecode:
function tb_json_encode(array $value, $options = 0) {
array_walk_recursive($value,'tb_json_encode');
return $value;
}
function tb_json_decode($value, $assoc = false, $depth = 512) {
array_walk_recursive($value,'tb_json_decode');
return $value;
}
function tb_urlencode(&$value, &$key) {
$key = urlencode($key);
$value = urlencode($value);
}
function tb_urldecode(&$value, &$key) {
$key = urldecode($key);
$value = urldecode($value);
}
第三种方式,在这儿就不详细介绍了。
参考:
经过测试发现PHP的递归实现是在是太慢了,于是尝试将地一种方案修改成了使用PHP内置的函数, 相比而言要好很多:
function tb_json_encode_ex($value, $options = 0)
{
array_walk_recursive($value, "tb_json_convert_encoding_g2u");
return json_encode($value);
}
function tb_json_decode_ex($value, $assoc = true, $depth = 512)
{
$value = json_decode($value);
array_walk_recursive($value, "tb_json_convert_encoding_u2g");
return $value;
}
function tb_json_convert_encoding_g2u(&$value, &$key)
{
$value = mb_convert_encoding($value, "UTF-8", "GBK");
}
function tb_json_convert_encoding_u2g(&$value, &$key)
{
$value = mb_convert_encoding($value, "GBK", "UTF-8");
}
事实上,第一种方式有一个优点是和基本的json_decode兼容,而第二种则不行。
第二种方式还可以有好多"变种",比如使用base64_encode/base64_decode 等等。