2009-10-12 78 views
13

我有一个包含javascript对象的序列化的PHP字符串:解析的Javascript(未JSON)在PHP

$string = '{fu:"bar",baz:["bat"]}'; 

实际字符串是复杂得多,当然,但仍远形成的JavaScript。这不是标准的JSON,所以json_decode失败。你知道任何PHP库,将解析这个字符串,并返回一个PHP关联数组?

+7

请解释你为什么不能JSON化它。这将简化所有事情。 – gnud 2009-10-12 11:47:23

+0

如上所述,这是* not *有效的JSON。有效的JSON要求对象键像字符串一样进行格式化,即用双引号括起来。 – Alsciende 2009-10-12 11:55:13

+0

是的,我明白了。我问你为什么不能在js中将它变成有效的json? – gnud 2009-10-12 11:55:42

回答

9

Services_JSON将解析该字符串(测试版本1.31)。但鉴于这是一个JSON解析器,并且这不是有效的JSON,你不能保证未来的版本仍然可以工作。

+0

谢谢,这是完美:) – Alsciende 2009-10-12 12:31:53

16

这听起来像一个有趣的挑战,所以我编写了一个小解析器:d

class JsParserException extends Exception {} 
function parse_jsobj($str, &$data) { 
    $str = trim($str); 
    if(strlen($str) < 1) return; 

    if($str{0} != '{') { 
     throw new JsParserException('The given string is not a JS object'); 
    } 
    $str = substr($str, 1); 

    /* While we have data, and it's not the end of this dict (the comma is needed for nested dicts) */ 
    while(strlen($str) && $str{0} != '}' && $str{0} != ',') { 
     /* find the key */ 
     if($str{0} == "'" || $str{0} == '"') { 
      /* quoted key */ 
      list($str, $key) = parse_jsdata($str, ':'); 
     } else { 
      $match = null; 
      /* unquoted key */ 
      if(!preg_match('/^\s*[a-zA-z_][a-zA-Z_\d]*\s*:/', $str, $match)) { 
      throw new JsParserException('Invalid key ("'.$str.'")'); 
      } 
      $key = $match[0]; 
      $str = substr($str, strlen($key)); 
      $key = trim(substr($key, 0, -1)); /* discard the ':' */ 
     } 

     list($str, $data[$key]) = parse_jsdata($str, '}'); 
    } 
    "Finshed dict. Str: '$str'\n"; 
    return substr($str, 1); 
} 

function comma_or_term_pos($str, $term) { 
    $cpos = strpos($str, ','); 
    $tpos = strpos($str, $term); 
    if($cpos === false && $tpos === false) { 
     throw new JsParserException('unterminated dict or array'); 
    } else if($cpos === false) { 
     return $tpos; 
    } else if($tpos === false) { 
     return $cpos; 
    } 
    return min($tpos, $cpos); 
} 

function parse_jsdata($str, $term="}") { 
    $str = trim($str); 


    if(is_numeric($str{0}."0")) { 
     /* a number (int or float) */ 
     $newpos = comma_or_term_pos($str, $term); 
     $num = trim(substr($str, 0, $newpos)); 
     $str = substr($str, $newpos+1); /* discard num and comma */ 
     if(!is_numeric($num)) { 
      throw new JsParserException('OOPSIE while parsing number: "'.$num.'"'); 
     } 
     return array(trim($str), $num+0); 
    } else if($str{0} == '"' || $str{0} == "'") { 
     /* string */ 
     $q = $str{0}; 
     $offset = 1; 
     do { 
      $pos = strpos($str, $q, $offset); 
      $offset = $pos; 
     } while($str{$pos-1} == '\\'); /* find un-escaped quote */ 
     $data = substr($str, 1, $pos-1); 
     $str = substr($str, $pos); 
     $pos = comma_or_term_pos($str, $term); 
     $str = substr($str, $pos+1);   
     return array(trim($str), $data); 
    } else if($str{0} == '{') { 
     /* dict */ 
     $data = array(); 
     $str = parse_jsobj($str, $data); 
     return array($str, $data); 
    } else if($str{0} == '[') { 
     /* array */ 
     $arr = array(); 
     $str = substr($str, 1); 
     while(strlen($str) && $str{0} != $term && $str{0} != ',') { 
      $val = null; 
      list($str, $val) = parse_jsdata($str, ']'); 
      $arr[] = $val; 
      $str = trim($str); 
     } 
     $str = trim(substr($str, 1)); 
     return array($str, $arr); 
    } else if(stripos($str, 'true') === 0) { 
     /* true */ 
     $pos = comma_or_term_pos($str, $term); 
     $str = substr($str, $pos+1); /* discard terminator */ 
     return array(trim($str), true); 
    } else if(stripos($str, 'false') === 0) { 
     /* false */ 
     $pos = comma_or_term_pos($str, $term); 
     $str = substr($str, $pos+1); /* discard terminator */ 
     return array(trim($str), false); 
    } else if(stripos($str, 'null') === 0) { 
     /* null */ 
     $pos = comma_or_term_pos($str, $term); 
     $str = substr($str, $pos+1); /* discard terminator */ 
     return array(trim($str), null); 
    } else if(strpos($str, 'undefined') === 0) { 
     /* null */ 
     $pos = comma_or_term_pos($str, $term); 
     $str = substr($str, $pos+1); /* discard terminator */ 
     return array(trim($str), null); 
    } else { 
     throw new JsParserException('Cannot figure out how to parse "'.$str.'" (term is '.$term.')'); 
    } 
} 

用法:

$data = '{fu:"bar",baz:["bat"]}';  
$parsed = array();  
parse_jsobj($data, $parsed);  
var_export($parsed); 

给出:

array (
    'fu' => 'bar', 
    'baz' => 
    array (
    0 => 'bat', 
), 
) 

与这些字符串测试:

'{fu:"bar",baz:["bat"]}', 
'{rec:{rec:{rec:false}}}', 
'{foo:[1,2,[3,4]]}', 
'{fu:{fu:"bar"},bar:{fu:"bar"}}', 
'{"quoted key":[1,2,3]}', 
'{und:undefined,"baz":[1,2,"3"]}', 
'{arr:["a","b"],"baz":"foo","gar":{"faz":false,t:"2"},f:false}', 
+0

这真的很好。两种说法:JavaScript对象键可以用简单的引号或双引号括起来(通常如果键不是有效的标识符(例如包含空格))。而undefined是一个有效的值。 – Alsciende 2009-10-12 12:55:40

+0

如果一个对象包含在另一个对象的值中并且strrpos不正确,例如'{fu:{fu:“bar”},bar:{fu:“bar”}}' – Alsciende 2009-10-12 13:00:17

+0

我认为现在,我已经修正了在数组中的字典和数组中的字典的一些错误。您的测试字符串现在可用 – gnud 2009-10-12 13:04:35

1

我发现Yii框架的CJSON::decode()函数也处理Javascript对象。

如果你不使用Yii,你应该能够只使用source code