2009-10-12 12 views
13

que tiene una cadena PHP que contiene la serialización de un objeto javascript:de análisis de Javascript (no JSON) en PHP

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

La cadena real es mucho más complicado, por supuesto, pero aún javascript bien formado. Esto no es JSON estándar, por lo que json_decode falla. ¿Conoces alguna biblioteca php que pueda analizar esta cadena y devolver una matriz asociativa php?

+7

Explique por qué no puede JSONify it. Eso simplificaría todo mucho. – gnud

+0

Como dije, este es * no * JSON válido. El JSON válido requiere que las claves de los objetos se formateen como cadenas, es decir, encerradas entre comillas dobles. – Alsciende

+0

Sí, tengo eso. Estaba preguntando por qué no puedes convertirlo en json válido en js? – gnud

Respuesta

9

Pear Services_JSON analizará esa cadena (versión probada 1.31). Pero dado que se trata de un analizador JSON y que este no es un JSON válido, no tiene garantía de que las versiones futuras sigan funcionando.

+0

Gracias, es perfecto :) – Alsciende

16

esto sonaba como un reto divertido, así que codifica un pequeño analizador: 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.')'); 
    } 
} 

Uso:

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

Da:

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

Probado con estas cadenas:

'{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

Es realmente muy agradable. Dos observaciones: las claves del objeto JavaScript se pueden encerrar entre comillas simples o comillas de dobles (por lo general, si la clave no es un identificador válido (por ejemplo, contiene un espacio)). Y undefined es un valor válido. – Alsciende

+0

Supongo que no funcionará si un objeto está incluido en el valor de otro objeto y strrpos no es correcto, por ejemplo '{fu: {fu: "bar"}, barra: {fu: "barra"}}' – Alsciende

+0

Creo que ahora, he corregido algunos errores para dicts en dicts y arrays en matrices. Su cadena de prueba funciona ahora. – gnud

1

Descubrí que la función CJSON::decode() de Yii-framework también maneja objetos Javascript.

Si no se está usando Yii, usted debería ser capaz de usar sólo el source code