Este es un breve artículo sobre los problemas más probables con json_encode y sus soluciones. A veces, al codificar datos en json, usando json_encode en php, obtenemos el resultado incorrecto que esperamos. Destaqué los tres problemas más comunes que enfrentan los programadores:
- acceso a los campos
- codificación de valor de texto
- valores numéricos
Acceso a los campos
El problema es que json_encode solo tiene acceso a los campos públicos del objeto. Por ejemplo, si tienes una clase
class Example { public $publicProperty; protected $protectedProperty; private $privateProperty; public function __construct($public, $protected, $private) { $this->publicProperty = $public; $this->protectedProperty = $protected; $this->privateProperty = $private; } }
entonces el resultado del siguiente código será:
$obj = new Example("some", "value", "here"); echo json_encode($obj);
Como puede ver, solo los campos públicos se incluyeron en el json resultante.
¿Qué hacer si necesita todos los campos?
Solución
Para php <5.4:
tendremos que implementar un método en la clase que devolverá el json terminado. Porque dentro de la clase hay acceso a todos los campos, puede formar la representación correcta del objeto para json_encode
class Example { public $publicProperty; protected $protectedProperty; private $privateProperty; public function __construct($public, $protected, $private) { $this->publicProperty = $public; $this->protectedProperty = $protected; $this->privateProperty = $private; } public function toJson() { return json_encode([ 'publicProperty' => $this->publicProperty, 'protectedProperty' => $this->protectedProperty, 'privateProperty' => $this->privateProperty, ]); } }
Para obtener json-a de un objeto, ahora necesita usar el método toJson , en lugar de aplicar directamente json_encode al objeto
$obj = new Example("some", "value", "here"); echo $obj->toJson();
Para php> = 5.4:
será suficiente implementar la interfaz JsonSerializable para nuestra clase, lo que implica agregar un método jsonSerialize que devolverá una estructura que representa el objeto para json_encode
class Example implements JsonSerializable { public $publicProperty; protected $protectedProperty; private $privateProperty; public function __construct($public, $protected, $private) { $this->publicProperty = $public; $this->protectedProperty = $protected; $this->privateProperty = $private; } public function jsonSerialize() { return [ 'publicProperty' => $this->publicProperty, 'protectedProperty' => $this->protectedProperty, 'privateProperty' => $this->privateProperty, ]; } }
Ahora podemos usar json_encode como antes
$obj = new Example("some", "value", "here"); echo json_encode($obj);
¿Por qué no usar el enfoque con el método toJson?
Muchos probablemente notaron que el enfoque para crear un método que devuelve json se puede usar en versiones de php> = 5.4. Entonces, ¿por qué no aprovecharlo? La cuestión es que su clase se puede usar como parte de una estructura de datos diferente.
echo json_encode([ 'status' => true, 'message' => 'some message', 'data' => new Example("some", "value", "here"), ]);
y el resultado ya será completamente diferente.
Además, la clase puede ser utilizada por otros programadores, para quienes este tipo de obtención de json de un objeto puede no ser completamente obvio.
¿Qué pasa si tengo muchos campos en clase?
En este caso, puede usar la función get_object_vars
class Example implements JsonSerializable { public $publicProperty; protected $protectedProperty; private $privateProperty; protected $someProp1; ... protected $someProp100500; public function __construct($public, $protected, $private) { $this->publicProperty = $public; $this->protectedProperty = $protected; $this->privateProperty = $private; } public function jsonSerialize() { $fields = get_object_vars($this);
¿Y si necesita campos privados de una clase que no se pueden editar?
Puede surgir una situación cuando necesita obtener campos privados ( privados , ya que el acceso a los campos protegidos se puede obtener a través de la herencia) en json. En este caso, será necesario utilizar la reflexión:
class Example { public $publicProperty = "someValue"; protected $protectedProperty; private $privateProperty1; private $privateProperty2; private $privateProperty3; public function __construct($privateProperty1, $privateProperty2, $privateProperty3, $protectedProperty) { $this->protectedProperty = $protectedProperty; $this->privateProperty1 = $privateProperty1; $this->privateProperty2 = $privateProperty2; $this->privateProperty3 = $privateProperty3; } } $obj = new Example("value1", 12, "21E021", false); $reflection = new ReflectionClass($obj); $public = []; foreach ($reflection->getProperties() as $property) { $property->setAccessible(true); $public[$property->getName()] = $property->getValue($obj); } echo json_encode($public);
Codificación de valores de texto
Cirílico y otros signos en UTF8
El segundo tipo de problema común de json_encode son los problemas de codificación. A menudo, los valores de texto que deben codificarse en json tienen caracteres en UTF8 (incluido el cirílico), como resultado, estos caracteres se presentarán en forma de códigos:
echo json_encode(" or ₳ ƒ 元 ﷼ ₨ ௹ ¥ ₴ £ ฿ $");
La visualización de dichos caracteres se trata de manera muy simple: al agregar el indicador JSON_UNESCAPED_UNICODE como segundo argumento a la función json_encode :
echo json_encode(" or ₳ ƒ 元 ﷼ ₨ ௹ ¥ ₴ £ ฿ $", JSON_UNESCAPED_UNICODE);
Símbolos en otras codificaciones
La función json_encode trata los valores de cadena como cadenas en UTF8, lo que puede causar un error si la codificación es diferente. Considere un pequeño fragmento de código (este ejemplo de código es lo más simple posible para demostrar una situación problemática)
echo json_encode(["p" => $_GET['p']]);
A primera vista, nada augura problemas, y ¿qué puede salir mal aquí? Yo también lo pensé. En la gran mayoría de los casos, todo funcionará, y por esta razón, encontrar el problema me llevó un poco más de tiempo cuando descubrí que el resultado de json_encode era falso.
Para recrear esta situación, suponga que p = % EF% F2% E8% F6% E0 (por ejemplo: localhost? =% EF% F2% E8% F6% E0 ).
* Las variables en matrices superglobales $ _GET y $ _REQUEST ya están decodificadas.
$decoded = urldecode("%EF%F2%E8%F6%E0"); var_dump(json_encode($decoded));
Como puede ver en el error: el problema con la codificación de la cadena transmitida (esto no es UTF8). La solución al problema es obvia: lleve el valor a UTF8
$decoded = urldecode("%EF%F2%E8%F6%E0"); $utf8 = utf8_encode($decoded); echo json_encode($utf8);
Valores numéricos
El último error típico está relacionado con la codificación de valores numéricos.
Por ejemplo:
echo json_encode(["string_float" => "3.0"]);
Como sabe, php no es un lenguaje estrictamente tipado y le permite usar números como una cadena, en la mayoría de los casos esto no conduce a errores dentro de la aplicación php. Pero dado que json se usa con mucha frecuencia para transferir mensajes entre aplicaciones, este formato para escribir números puede causar problemas en otra aplicación. Es recomendable utilizar el indicador JSON_NUMERIC_CHECK :
echo json_encode(["string_float" => "3.0"], JSON_NUMERIC_CHECK);
Ya mejor. Pero como puede ver, "3.0" se convirtió en 3, que en la mayoría de los casos se interpretará como int. Utilizamos una bandera más JSON_PRESERVE_ZERO_FRACTION para la conversión correcta a flotante:
echo json_encode(["string_float" => "3.0"], JSON_NUMERIC_CHECK | JSON_PRESERVE_ZERO_FRACTION);
También le pido que preste atención al siguiente fragmento de código, que ilustra una serie de posibles problemas con json_encode y valores numéricos:
$data = [ "0000021",
Gracias por leer
Estaré encantado de ver en los comentarios una descripción de los problemas que encontró que no se mencionaron en el artículo