Este é um pequeno artigo sobre os problemas mais prováveis com o json_encode e suas soluções. Às vezes, ao codificar dados no json, usando json_encode no php, obtemos o resultado errado que esperamos. Eu destaquei os três problemas mais comuns que os programadores enfrentam:
- acesso a campos
- codificação de valor de texto
- valores numéricos
Acesso a campos
O problema é que json_encode tem acesso apenas aos campos públicos do objeto. Por exemplo, se você tem uma turma
class Example { public $publicProperty; protected $protectedProperty; private $privateProperty; public function __construct($public, $protected, $private) { $this->publicProperty = $public; $this->protectedProperty = $protected; $this->privateProperty = $private; } }
o resultado do seguinte código será:
$obj = new Example("some", "value", "here"); echo json_encode($obj);
como você pode ver, apenas campos públicos foram incluídos no json resultante.
O que fazer se você precisar de todos os campos?
Solução
Para php <5.4:
precisaremos implementar um método na classe que retornará o json finalizado. Porque dentro da classe há acesso a todos os campos, você pode formar a representação correta do 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 obter o json-a de um objeto, agora você precisa usar o método toJson , em vez de aplicar diretamente o json_encode ao objeto
$obj = new Example("some", "value", "here"); echo $obj->toJson();
Para php> = 5.4:
será suficiente implementar a interface JsonSerializable para nossa classe, o que implica adicionar um método jsonSerialize que retornará uma estrutura representando o 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, ]; } }
Agora podemos usar json_encode como antes
$obj = new Example("some", "value", "here"); echo json_encode($obj);
Por que não usar a abordagem com o método toJson?
Muitos provavelmente notaram que a abordagem para criar um método retornando json pode ser usada nas versões do php> = 5.4. Então, por que não tirar proveito disso? O problema é que sua classe pode ser usada como parte de uma estrutura de dados diferente.
echo json_encode([ 'status' => true, 'message' => 'some message', 'data' => new Example("some", "value", "here"), ]);
e o resultado já será completamente diferente.
Além disso, a classe pode ser usada por outros programadores, para quem esse tipo de obtenção de json de um objeto pode não ser completamente óbvio.
E se eu tiver muitos campos em sala de aula?
Nesse caso, você pode usar a função 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);
E se você precisar de campos particulares de uma classe que não pode ser editada?
Uma situação pode surgir quando você precisa obter campos particulares ( privados , pois o acesso a campos protegidos pode ser obtido por herança) no json. Nesse caso, será necessário usar a reflexão:
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);
Codificação de valores de texto
Sinais cirílicos e outros em UTF8
O segundo tipo de problema comum json_encode é o de codificação. Geralmente, os valores de texto que precisam ser codificados em json possuem caracteres em UTF8 (incluindo cirílico); como resultado, esses caracteres serão apresentados na forma de códigos:
echo json_encode(" or ₳ ƒ 元 ﷼ ₨ ௹ ¥ ₴ £ ฿ $");
A exibição desses caracteres é tratada com muita simplicidade - adicionando o sinalizador JSON_UNESCAPED_UNICODE como o segundo argumento à função json_encode :
echo json_encode(" or ₳ ƒ 元 ﷼ ₨ ௹ ¥ ₴ £ ฿ $", JSON_UNESCAPED_UNICODE);
Símbolos em outras codificações
A função json_encode trata os valores de sequência como sequências em UTF8, o que pode causar um erro se a codificação for diferente. Considere um pequeno pedaço de código (este exemplo de código é o mais simples possível para demonstrar uma situação problemática)
echo json_encode(["p" => $_GET['p']]);
À primeira vista, nada indica problemas e o que pode dar errado aqui? Eu também pensei. Na grande maioria dos casos, tudo funcionará e, por esse motivo, encontrar o problema demorou um pouco mais quando encontrei o resultado de json_encode pela primeira vez.
Para recriar essa situação, suponha que p = % EF% F2% E8% F6% E0 (por exemplo: host local? =% EF% F2% E8% F6% E0 ).
* Variáveis nas matrizes superglobais $ _GET e $ _REQUEST já estão decodificadas.
$decoded = urldecode("%EF%F2%E8%F6%E0"); var_dump(json_encode($decoded));
Como você pode ver no erro: o problema com a codificação da string transmitida (isso não é UTF8). A solução para o problema é óbvia - traga o valor para UTF8
$decoded = urldecode("%EF%F2%E8%F6%E0"); $utf8 = utf8_encode($decoded); echo json_encode($utf8);
Valores numéricos
O último erro típico está relacionado à codificação de valores numéricos.
Por exemplo:
echo json_encode(["string_float" => "3.0"]);
Como você sabe, php não é uma linguagem estritamente digitada e permite que você use números como uma string; na maioria dos casos, isso não leva a erros dentro do aplicativo php. Mas como o json é frequentemente usado para transferir mensagens entre aplicativos, esse formato para escrever números pode causar problemas em outro aplicativo. É aconselhável usar o sinalizador JSON_NUMERIC_CHECK :
echo json_encode(["string_float" => "3.0"], JSON_NUMERIC_CHECK);
Já está melhor. Mas como você pode ver, "3.0" se transformou em 3, que na maioria dos casos será interpretado como int. Usamos mais um sinalizador JSON_PRESERVE_ZERO_FRACTION para a conversão correta para flutuar:
echo json_encode(["string_float" => "3.0"], JSON_NUMERIC_CHECK | JSON_PRESERVE_ZERO_FRACTION);
Também peço que você preste atenção no seguinte fragmento de código, que ilustra vários problemas possíveis com json_encode e valores numéricos:
$data = [ "0000021",
Obrigado pela leitura.
Ficarei feliz em ver nos comentários uma descrição dos problemas que você encontrou e que não foram mencionados no artigo