首页 > PHP,Javascript > php使用json与js交换数据——不带双引号的json数据解析

php使用json与js交换数据——不带双引号的json数据解析

PHP和Javascript在某些方面总是惊人的相似,可是有时候却让人摸不着头脑。

案例1:Javascript原生支持的json格式,在编码上比PHP体积小,例如

<?php
$arr = array(array("name" => "php"));
$json = json_encode($arr);
echo $json;
//会输出[{"name":"php"}]
?>

而在Js中,上面的案例[{"name":"php"}]是可以被任何浏览器识别的,但是Javascript还支持更简单的写法

var string = [{name:"php"}];

js可以省略属性名双引号,所以js占用体积小。可是,当我们使用js构造的json字符没有双引号直接传递给php之后,例如:

<?php
$json = '[{name:"php"}]';
$brr = json_decode($json, true);
var_dump($brr);
//坑爹的php竟然输出NULL
?>

介于这两种语言的差异,可以使用正则表达式来修正。

<?php
$json = '[{name:"php"}]';
$php = preg_replace('/(\w+)\s*:\s*/is', '"$1":', $json);
$crr = json_decode($php, true);
//这样就能正确解析js传入的json了

当然,如果你希望js从php获取的json的key不带双引号以节省传输量,还可以这样:

<?php
$arr = array(array("name" => "php"));
$json = json_encode($arr);
$js = preg_replace('/"(\w+)"\s*:\s*/is', '$1:', $json);
echo $js;
//将会输出:[{name:"php"}]


重大更新:如果你的数据非常简单,键值对中没有冒号,单双引号等特殊字符,上面的即可满足,可是一旦出现这些特殊符号,正则匹配将会出错。下面的复杂正则表达式希望可以解决这些问题:

<?php
header("content-type:text/html; charset=utf-8"); 
//变态情况下的数据
$json = '[{ id : \'301\' , pId : 11 ,name:"%u5730%u7403%u6751%2819%29",url:"#",target:"_self",iconSkin:"pIcon03"},{id:"111",pId:"11",name:"PK%289%29",url:"#",target:"_self",iconSkin:"pIcon03"},{id:"1",pId:0,name:"%u9986%u85CF%u5206%u7C7B",url:"javascript:void(0);",target:"_self",open:true,iconSkin:"pIcon01"}]';
$arr = json_decode_fix($json);
var_dump($arr);
//极有可能出现的情况
$json = '[{a:"3:4"},{b:"7:8"}]';
$brr = json_decode_fix($json);
var_dump($brr);


/**
 * php解码js版本的json,兼容单引号和不带双引号情况
 * @param string $json
 * @return array
 */
function json_decode_fix($json)
{
	$json = str_replace("',", '",', $json);
	// 修复子元素带有'{"
	if(($first = strpos($json, "'{\"")) !== false) {
		$json = str_replace("'{\"", "'{\\\"", $json);
		$last = strpos($json, "}");
		$middle = str_replace('":', '\":', substr($json, $first, $last-$first));
		$json = substr_replace($json, $middle, $first, $last-$first);
	}
	
    $result = json_decode($json, true);
    if($result)
    {
        return $result;
    }else{
        //不允许以纯数字作为键名或在键名或值处出现冒号,否则可能修复失败
        $pattern = '/(\w+\s*):([\s*\"\'|\d*]|true|false)+/is';
        $json = preg_replace_callback($pattern, function($matches){
            //处理纯数字或布尔值,其冒号之后为true、false、纯数字而非单双引号
            $index = strpos($matches[0], ':');
            $key = trim($matches[0]);
            $last = substr($key, -1);
            if($last === '"')
            {
				if(strpos($key, '""') !== false) {
					$key = rtrim($key, '" ').'"';
				}else{
					$key = rtrim($key, '" ');
				}
                return '"'.trim(substr($key, 0, $index)).'":'.trim(substr($key, $index+1)).'"';
            }else if($last === "'")
            {
                $key = rtrim($key, "' ");
                return '"'.trim(substr($key, 0, $index)).'":"'.trim(substr($key, $index+1), "' ").(substr($key, $index+1) === false ? '' : '"');
            }else{
                $left = trim(substr($key, 0, $index));
                return is_numeric($left) ? $matches[0] : '"'.trim(substr($key, 0, $index)).'":'.substr($key, $index+1);
            }
        }, $json);
        return json_decode($json, true);
    }
}

费了九牛二虎之力才编写出这个函数,测试虽然也花了很长时间,但是也不能面面俱到。

以上函数中,针对特殊函数类型如布尔值true和false,整数类型纯数字不带双引号的情况做了兼容。此外,针对值中含有冒号等特殊字符的情况也做了特殊处理。


案例2:PHP使用json_encode会导致中文汉字被编码为unicode,输出到js中虽然可以被识别,但是可读性大大降低。例如

<?php
$arr = array(array("word" => "中文"));
$json = json_encode($arr);
echo $json;
//会输出[{"word":"\u4e2d\u6587"}]

这时,通过js返回给浏览器时,中文会以unicode存在,对调试工作带来不便。这里介绍一个自动识别中文且不编码的方法,代码如下:

<?php
/**
 *@功能:json_encode不编译中文成unicod
 *@参数:类型为array,$array是待编码的数组
 *@返回:类型为string
 */
function json_encode_cn($array)
{
	arrayRecursive($array, 'urlencode', true);
	$json = json_encode($array);
	return urldecode($json);
}

/**************************************************************
   *
   *    使用特定function对数组中所有元素做处理
   *    @param  string  &$array     要处理的字符串
   *    @param  string  $function   要执行的函数
   *    @return boolean $apply_to_keys_also     是否也应用到key上
   *    @access public
   *
 *************************************************************/
function arrayRecursive(&$array, $function, $apply_to_keys_also = false)
{
	static $recursive_counter = 0;
	if (++$recursive_counter > 1000) {
		die('possible deep recursion attack');
	}
	if(!empty($array))
	{
		foreach ($array as $key => $value) {
			if (is_array($value)) {
				arrayRecursive($array[$key], $function, $apply_to_keys_also);
			} else {
				$array[$key] = $function($value);
			}
	 
			if ($apply_to_keys_also && is_string($key)) {
				$new_key = $function($key);
				if ($new_key != $key) {
					$array[$new_key] = $array[$key];
					unset($array[$key]);
				}
			}
		}
	}
	$recursive_counter--;
}

案例2可以改为:

header("content-type:text/html; charset=utf-8"); 
$arr = array(array("word" => "中文"));
$json = json_encode_cn($arr);
echo $json;
//会输出[{"word":"中文"}]

调试完成之后,上线的时候将json_encode_cn批量替换为json_encode即可,不替换也应该不会影响使用。

永久链接:http://blog.zhengshuiguang.com/php/json_decode_fix.html

转载随意!带上文章地址吧。

标签:json_encode 中文编码 unicode json json_decode_fix

评论已关闭