Resolvendo problemas típicos com json_encode (PHP)

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); // {"publicProperty":"some"} 

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); // {"publicProperty":"some","protectedProperty":"value","privateProperty":"here"} 

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); // -  ... return $fields; } } 

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); //{"publicProperty":"someValue","protectedProperty":false,"privateProperty1":"value1","privateProperty2":12,"privateProperty3":"21E021"} 

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 ₳ ƒ 元 ﷼ ₨ ௹ ¥ ₴ £ ฿ $"); // "\u043a\u0438\u0440\u0438\u043b\u043b\u0438\u0446\u0430 or \u20b3 \u0192 \u5143 \ufdfc \u20a8 \u0bf9 \uffe5 \u20b4 \uffe1 \u0e3f \uff04" 

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); // " or ₳ ƒ 元 ﷼ ₨ ௹ ¥ ₴ £ ฿ $" 

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)); // bool(false) var_dump(json_last_error_msg()); // string(56) "Malformed UTF-8 characters, possibly incorrectly encoded" 

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"]); // {"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); // {"string_float":3} 

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); // {"string_float":3.0} 

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", //   6.12345678910111213, //     ( ) "+81011321515", //  "21E021", //   ]; echo json_encode($data, JSON_NUMERIC_CHECK); //[ // 21, // 6.1234567891011, // 81011321515, // 2.1e+22 // ] 

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

Source: https://habr.com/ru/post/pt483492/


All Articles