解决json_encode(PHP)的典型问题

这是一篇有关json_encode最可能出现的问题及其解决方案的简短文章。 有时在json中编码数据时,在php中使用json_encode ,我们会得到我们期望的错误结果。 我强调了程序员面临的三个最常见的问题:


  • 进入领域
  • 文字值编码
  • 数值

进入领域


问题是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; } } 

那么以下代码的结果将是:


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

如您所见,在结果json中仅包含公共字段。
如果需要所有字段怎么办?


解决方案


对于php <5.4:
我们将需要在该类中实现一个方法,该方法将返回完成的json。 因为 在类中可以访问所有字段,您可以为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, ]); } } 

要从对象获取json-a,您现在需要使用toJson方法,而不是直接将json_encode应用于对象


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

对于php> = 5.4:
足以为我们的类实现JsonSerializable接口,这意味着添加一个jsonSerialize方法,该方法将返回表示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, ]; } } 

现在我们可以像以前一样使用json_encode


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

为什么不将方法与toJson方法一起使用?


许多人可能已经注意到,用于创建返回json方法的方法可以在php> = 5.4版本中使用。 那么为什么不利用它呢? 问题是您的类可以用作其他数据结构的一部分。


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

结果将是完全不同的。
同样,该类可由其他程序员使用,对于他们来说,从对象获取json的这种类型可能并不十分明显。



如果我在课堂上有很多领域怎么办?


在这种情况下,可以使用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; } } 

并且,如果您需要无法编辑的班级中的私有字段?


当您需要在json中获取私有字段( private ,因为可以通过继承获取对受保护字段的访问)时,可能会出现这种情况。 在这种情况下,有必要使用反射:


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

文字值编码


UTF8中的西里尔字母和其他符号


常见的json_encode问题的第二种类型是编码问题。 通常需要在json中编码的文本值在UTF8中包含字符(包括西里尔字母),因此,这些字符将以代码形式显示:


 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" 

此类字符的显示非常简单-通过将JSON_UNESCAPED_UNICODE标志作为 json_encode函数第二个参数添加:


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

其他编码中的符号


json_encode函数将字符串值视为UTF8中的字符串,如果编码不同,则可能导致错误。 考虑一小段代码(此代码示例尽可能简单地演示问题情况)


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

乍一看,没有什么预示着问题,这里可能出什么问题? 我也是这么想的 在绝大多数情况下,一切都会正常工作,因此,当我第一次遇到json_encode的结果为false时,发现问题使我花了更长的时间。


要重新创建这种情况,假设p = %EF%F2%E8%F6%E0 (例如: localhost?=%EF%F2%E8%F6%E0 )。
*超全局数组$ _GET和$ _REQUEST中的变量已被解码。


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

从错误中可以看出:传输字符串的编码存在问题(这不是UTF8)。 解决问题的方法很明显-将值带到UTF8


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

数值


最后一个典型的错误与数值编码有关。


例如:


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

如您所知,php不是严格类型的语言,它允许您将数字用作字符串,在大多数情况下,这不会导致php应用程序内部出现错误。 但是由于json通常用于在应用程序之间传输消息,因此这种写数字的格式可能会在另一个应用程序中引起问题。 建议使用JSON_NUMERIC_CHECK标志:


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

已经更好了。 但是正如您所看到的,“ 3.0”变成3,在大多数情况下将被解释为int。 我们再使用一个标志JSON_PRESERVE_ZERO_FRACTION来正确转换为float:


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

我还请您注意以下代码片段,它说明了json_encode和数值可能出现的许多问题:


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

感谢您的阅读。


我很高兴在评论中看到您所遇到的问题的描述,而本文中并未提到

Source: https://habr.com/ru/post/zh-CN483492/


All Articles