Introducción a JSON usando PHP

JSON (JavaScript Object Notation) es un formato de intercambio de datos ligero (como XML pero sin el marcado). Su sintaxis es un subconjunto del lenguaje JavaScript estandarizado en el año 1999, se puede manejar de forma nativa en JavaScript.

JSON actúa como un nexo entre el servidor y la aplicación del lado del cliente. Además, como sintácticamente es muy simple, se tienen que transmitir menos bytes en cada transacción.

En aplicaciones modernas JSON ha sustituído en bastantes situaciones a XML, y frameworks del lado del cliente como backbone.js, ember.js lo utilizan mucho en sus estructuras.

JSON

Al ser JSON un subconjunto del lenguaje JavaScript, comparte algunas construcciones del lenguaje. En JSON se pueden guardar combinaciones desordenadas de keys (llaves) y values (valores) en objetos o utilizar arrays para preservar el orden de los valores.

Esto lo hace fácil de analizar y de leer, pero también tiene algunas limitaciones, JSON sólo define una cantidad pequeña de data types.

Los datatypes que soporta JSON son: strings, números, booleanos y null, además de soportar objetos y arrays como valores.

{  
   "titulo":"Este es un artículo",
   "visitas":410,
   "publicado":true,
   "categoria":null,
   "comentarios":[  
      {  
         "autor":"Pedro Diaz",
         "mensaje":"excelente artículo"
      },
      {  
         "autor":"Carlos Flores",
         "mensaje":"Artículo deficiente"
      }
   ]
}

En el ejemplo se visualiza todas las posibilidades de JSON. Es necesario asegurarse de que el documento JSON esté codificado en UTF-8, esto se realizará luego para este caso con PHP. Se desarrolló también BSON (Binary JSON), el cual esta diseñado para ser más eficiente y proveer características como soporte de fechas. BSON se utiliza por ejemplo en bases de datos no relacionales como MongoDB.

Debido a que PHP tiene un manejo de tipos bastante más extenso que JSON, es necesario codificar y decodificar de forma adecuada los documentos JSON en PHP con las modificaciones pertinentes. Por ejemplo si queremos transportar objetos fecha, hay que elegir entre enviar un unix timestamp o preformatear la fecha en un string con alguna función como strftime().

Codificar JSON en PHP

Con json_encode se puede traducir cualquier cosa codificada en UTF-8 de PHP a un string JSON, exceptuando arrays puros (éstos en PHP son arrays con un index numérico y ordenado) se convierte en un objeto con keys y values.

json_encode (mixed $value, int $options = 0);

Las opciones $options son un bitmask, que permite cambiar la forma en que se codifican los datos. En los siguientes ejemplos no lo vamos a emplear, por lo que se utilizan los valores por defecto.

Empezando con los tipos básicos:


json_encode(array("Azul", "Blanco", "Amarillo"));
// Devuelve: ["Azul", "Blanco", "Amarillo"];
json_encode(array(2=>"dos", 10=>"diez"));
// Devuelve {"2":"dos", "10"=>"diez"}
json_encode(array("Azul"=> true, "Amarillo"=>null));
// Devuelve: {"Azul":true, "Amarillo":null}

Cómo se traducen los arrays depende del index que se utilice. Puede verse que _jsonencode() traduce los tipos correctamente en el caso de booleano y null.


class Usuario {
public $nombre = "";
public $apellido = "";
public $fechaNacimiento = "";
}
$usuario = new Usuario;
$usuario->nombre = "Angela";
$usuario->apellido = "Baca";
json_encode($usuario);
// Devuelve: {"nombre": "Angela", "apellido": "Baca"}
$usuario->fechaNacimiento = new DateTime();
json_encode($usuario);
/*
Devuelve:
{
"nombre":"Angela",
"apellido":"Baca",
"fechaNacimiento":{
"date":"1990-10-10 10:31:22",
"timezone_type":3,
"timezone":"America\/Lima"
}
}
*/

Los objetos se inspeccionan y sus atributos públicos se convierten. Esto ocurre de forma recursiva, por lo que en el ejemplo anterior los atributos del objeto DateTime también se traducen en JSON.

Esta es una forma de transmitir fácilmente fechas en JSON, el lado del cliente podrá tomar el tiempo real y el timezone.


class Usuario {
// Nombre publico, apellido vacío y fecha de nacimiento sin establecer
public $nombre = "Angela";
public $apellido = "";
public $fechaNacimiento;
// Direccion protected
protected $direccion = "Los Sureños";
// Pais private
private $pais = "Perú";
// Funcion closure
public $funcion;
// Constructor
public function __construct(){
$this->funcion = function(){
return "Hey!";
};
}
}
$usuario = new Usuario;
var_dump(json_encode($usuario));
/*
Devuelve:
string '{"nombre":"Angela","apellido":"","fechaNacimiento":null,"funcion":{}}'
*/

Se puede comprobar que sólo se utilizan los atributos públicos, variables no iniciadas se traducen en null y los closures de un atributo público se codifican como un objeto vacío.

El bitmask $option

Los bitmasks se utilizan para establecer ciertas banderas On y Off en una llamada a una función. Si se quiere establecer una opción, se le debe agregar la constante como argumento. Si se quiere combinar dos o más opciones, combinarlas con el operador || o OR.


json_encode(array("Braveheart", "1995"), JSON_NUMERIC_CHECK | JSON_FORCE_OBJECT);
// Devuelve: {"0": "Braveheart", "1": 1995}

JSON_FORCE_OBJECT fuerza el array a ser traducido en un objeto y JSON_NUMERIC_CHECK convierte números dentro de strings en números. La lista de bitmasks es esta. Muchos de ellos sirven para tratar con caracteres como <, >, & o “”.

También hay una constante que sirve para ver los resultados más claros con espacios en blanco, JSON_PRETTY_PRINT (no recomendable en producción ya que añade caracteres innecesarios).

Decodificar JSON en PHP

Decodificar JSON en PHP es tan sencillo como codificar. PHP proporciona la función _jsondecode se encarga de ello. Simplemente pasando un string JSON válido en el método se obtiene un objeto stdClass.


$string = '{"nombre": "Angelina", "apellido": "Jolie"}';
$resultado = json_decode($string);
// Vemos el resultado de la decodificación:
var_dump($resultado);
/*
* object(stdClass)[3]
public 'nombre' => string 'Angelina' (length=8)
public 'apellido' => string 'Jolie' (length=5)
*/
echo $resultado->nombre; // Angelina
echo $resultado->apellido; // Jolie

Si lo que quieres es obtener un array asociativo, simplemente hay que poner un segundo parámetro como true:


$string = '{"nombre": "Angelina", "apellido": "Jolie"}';
$resultado = json_decode($string, true);
// Vemos el resultado de la decodificación:
var_dump($resultado);
/*
* array (size=2)
'nombre' => string 'Angelina' (length=8)
'apellido' => string 'Jolie' (length=5)
*/
echo $resultado['nombre']; // Angelina
echo $resultado['apellido']; // Jolie

Si se espera un archivo JSON muy extenso, se puede limitar la profundidad de recursión a un nivel determinado. La función devolverá null y dejará de analizar si el documento es más profundo que el nivel marcado:


$string = '{"nombre": {"Angelina": {"apellido": "Jolie"}}}';
$resultado = json_decode($string, true, 2);
// Vemos el resultado de la decodificación:
var_dump($resultado); // Devuelve: null

El último argumento funciona de la misma forma que en _jsonencode, pero sólo está la opción de JSON_BIGINT_AS_STRING, que convierte bigints en strings.

Manejo de errores y testing

Si el valor JSON no se ha podido analizar o la profundidad de recursión es mayor de la indicada (como en el ejemplo anterior), se obtiene null de json_decode. Esto significa que no surge ninguna excepción directamente de _jsonencode o _jsondecode.

Para el manejo de errores de emplea la función _json_lasterror, que devuelve un integer con un código de error que puede ser uno de los siguientes:

JSON_ERROR_NONE: No ha habido ningún error.
JSON_ERROR_DEPTH: El máximo nivel de profundidad se ha excedido.
JSON_ERROR_STATE_MISMATCH: JSON inválido o mal formado.
JSON_ERROR_CTRL_CHAR: Error de control de caracteres, posiblemente incorrectamente codificado.
JSON_ERROR_SYNTAX: Error de sintaxis.
JSON_ERROR_UTF8: Caracteres UTF-8 mal formados, posiblemente incorrectamente codificado.

Con dicha información se puede escribir un método helper que proporciona una descripción cuando ocurre un error:


class JsonHandler
{
// Array con los mensajes de error
protected static $mensajes = array(
JSON_ERROR_NONE => 'No ha habido ningún error',
JSON_ERROR_DEPTH => 'Se ha alcanzado el máximo nivel de profundidad',
JSON_ERROR_STATE_MISMATCH => 'JSON inválido o mal formado',
JSON_ERROR_CTRL_CHAR => 'Error de control de caracteres, posiblemente incorrectamente codificado',
JSON_ERROR_SYNTAX => 'Error de sintaxis',
JSON_ERROR_UTF8 => 'Caracteres UTF-8 mal formados, posiblemente incorrectamente codificado'
);
// Codificar
public static function encode($value, $options = 0){
$result = json_encode($value, $options);
if ($result) {
return $result;
}
throw new RuntimeException(static::$mensajes[json_last_error()]);
}
// Decodificar
public static function decode($json, $assoc = false){
$result = json_decode($json, $assoc);
if ($result) {
return $result;
}
throw new RuntimeException(static::$mensajes[json_last_error()]);
}
}

Ahora podemos utilizar la clase:


$string = '{"nombre}}';
$resultado = JsonHandler::decode($string);
var_dump($resultado);
// Devuelve: Fatal error: Uncaught exception 'RuntimeException' with message 'Error de sintaxis'



Share on FacebookTweet about this on TwitterShare on Google+

Agregue un comentario

Su dirección de correo no se hará público. Los campos requeridos están marcados *