Résolution de problèmes typiques avec json_encode (PHP)

Il s'agit d'un court article sur les problèmes les plus probables avec json_encode et leurs solutions. Parfois, lors du codage des données en json, en utilisant json_encode en php, nous obtenons le mauvais résultat que nous attendons. J'ai souligné les trois problèmes les plus courants auxquels sont confrontés les programmeurs:


  • accès aux champs
  • encodage de la valeur du texte
  • valeurs numériques

Accès aux champs


Le problème est que json_encode n'a accès qu'aux champs publics de l'objet. Par exemple, si vous avez une classe


class Example { public $publicProperty; protected $protectedProperty; private $privateProperty; public function __construct($public, $protected, $private) { $this->publicProperty = $public; $this->protectedProperty = $protected; $this->privateProperty = $private; } } 

alors le résultat du code suivant sera:


 $obj = new Example("some", "value", "here"); echo json_encode($obj); // {"publicProperty":"some"} 

comme vous pouvez le voir, seuls les champs publics ont été inclus dans le json résultant.
Que faire si vous avez besoin de tous les champs?


Solution


Pour php <5,4:
nous devrons implémenter une méthode dans la classe qui retournera le json fini. Parce que à l'intérieur de la classe, il y a accès à tous les champs, vous pouvez former la représentation correcte de l'objet pour 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, ]); } } 

Pour obtenir json-a à partir d'un objet, vous devez maintenant utiliser la méthode toJson , plutôt que d'appliquer directement json_encode à l'objet


 $obj = new Example("some", "value", "here"); echo $obj->toJson(); 

Pour php> = 5,4:
il suffira d'implémenter l'interface JsonSerializable pour notre classe, ce qui implique l'ajout d'une méthode jsonSerialize qui retournera une structure représentant l'objet pour 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, ]; } } 

Maintenant, nous pouvons utiliser json_encode comme avant


 $obj = new Example("some", "value", "here"); echo json_encode($obj); // {"publicProperty":"some","protectedProperty":"value","privateProperty":"here"} 

Pourquoi ne pas utiliser l'approche avec la méthode toJson?


Beaucoup ont probablement remarqué que l'approche de création d'une méthode renvoyant json peut être utilisée dans les versions de php> = 5.4. Alors pourquoi ne pas en profiter? Le fait est que votre classe peut être utilisée dans le cadre d'une structure de données différente.


 echo json_encode([ 'status' => true, 'message' => 'some message', 'data' => new Example("some", "value", "here"), ]); 

et le résultat sera déjà complètement différent.
En outre, la classe peut être utilisée par d'autres programmeurs, pour qui ce type d'obtention de json à partir d'un objet peut ne pas être complètement évident.



Et si j'ai beaucoup de champs en classe?


Dans ce cas, vous pouvez utiliser la fonction 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; } } 

Et si vous avez besoin de champs privés d'une classe qui ne peuvent pas être modifiés?


Une situation peut survenir lorsque vous devez obtenir des champs privés ( privés , car l'accès aux champs protégés peut être obtenu par héritage) dans json. Dans ce cas, il faudra utiliser la réflexion:


 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"} 

Encodage des valeurs de texte


Signes cyrilliques et autres en UTF8


Le deuxième type de problème json_encode courant est celui des problèmes de codage. Souvent, les valeurs de texte qui doivent être codées en json ont des caractères en UTF8 (y compris cyrillique), par conséquent, ces caractères seront présentés sous la forme de codes:


 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" 

L'affichage de ces caractères est traité très simplement - en ajoutant l'indicateur JSON_UNESCAPED_UNICODE comme deuxième argument de la fonction json_encode :


 echo json_encode(" or ₳ ƒ 元 ﷼ ₨ ௹ ¥ ₴ £ ฿ $", JSON_UNESCAPED_UNICODE); // " or ₳ ƒ 元 ﷼ ₨ ௹ ¥ ₴ £ ฿ $" 

Symboles dans d'autres encodages


La fonction json_encode traite les valeurs de chaîne comme des chaînes en UTF8, ce qui peut provoquer une erreur si le codage est différent. Considérons un petit morceau de code (cet exemple de code est aussi simple que possible pour illustrer une situation problématique)


 echo json_encode(["p" => $_GET['p']]); 

À première vue, rien ne présage de problèmes, et qu'est-ce qui peut mal tourner ici? Je le pensais aussi. Dans la grande majorité des cas, tout fonctionnera, et pour cette raison, trouver le problème m'a pris un peu plus de temps lorsque j'ai rencontré le résultat de json_encode était faux.


Pour recréer cette situation, supposons que p = % EF% F2% E8% F6% E0 (par exemple: localhost? =% EF% F2% E8% F6% E0 ).
* Les variables des tableaux superglobaux $ _GET et $ _REQUEST sont déjà décodées.


 $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" 

Comme vous pouvez le voir sur l'erreur: le problème avec l'encodage de la chaîne transmise (ce n'est pas UTF8). La solution au problème est évidente - apporter de la valeur à UTF8


 $decoded = urldecode("%EF%F2%E8%F6%E0"); $utf8 = utf8_encode($decoded); echo json_encode($utf8); // "ïòèöà" 

Valeurs numériques


La dernière erreur typique est liée au codage des valeurs numériques.


Par exemple:


 echo json_encode(["string_float" => "3.0"]); // {"string_float":"3.0"} 

Comme vous le savez, php n'est pas un langage strictement typé et vous permet d'utiliser des nombres sous forme de chaîne, dans la plupart des cas, cela ne conduit pas à des erreurs dans l'application php. Mais comme json est très souvent utilisé pour transférer des messages entre applications, ce format d'écriture de nombres peut provoquer des problèmes dans une autre application. Il est conseillé d'utiliser l'indicateur JSON_NUMERIC_CHECK :


 echo json_encode(["string_float" => "3.0"], JSON_NUMERIC_CHECK); // {"string_float":3} 

Déjà mieux. Mais comme vous pouvez le voir, "3.0" est devenu 3, qui dans la plupart des cas sera interprété comme int. Nous utilisons un indicateur JSON_PRESERVE_ZERO_FRACTION de plus pour une conversion correcte en float:


 echo json_encode(["string_float" => "3.0"], JSON_NUMERIC_CHECK | JSON_PRESERVE_ZERO_FRACTION); // {"string_float":3.0} 

Je vous demande également de prêter attention au fragment de code suivant, qui illustre un certain nombre de problèmes possibles avec json_encode et les valeurs numériques:


 $data = [ "0000021", //   6.12345678910111213, //     ( ) "+81011321515", //  "21E021", //   ]; echo json_encode($data, JSON_NUMERIC_CHECK); //[ // 21, // 6.1234567891011, // 81011321515, // 2.1e+22 // ] 

Merci d'avoir lu.


Je serai heureux de voir dans les commentaires une description des problèmes que vous avez rencontrés qui n'ont pas été mentionnés dans l'article

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


All Articles