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_r("var ret = " +"\"\u4e2d\u56fd\u4eba\";");} catch(e){}; alert(ret);

再来看看S(Server)端,json_encode/json_decode会假定给定的数据是UTF-8编码的,这儿就有几种思路了:

  1. 将数据转化为UTF-8编码的,然后再调用json_encode, 或者首先json_decode,然后再转化为GBK编码的。
  2. 将数据urlencode,这样所有的数据都是ASCII的了,调用json_encode就没有问题了,json_decode之后也需要 urldecode下。
  3. 自己编写函数将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);

}

第三种方式,在这儿就不详细介绍了。

参考:

  1. http://gggeek.altervista.org/sw/article_20061113.html
  2. http://json.org

经过测试发现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 等等。