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
转载随意!带上文章地址吧。
评论已关闭