将PHP对象序列化为JSON

所以当我偶然发现新的JsonSerializable接口时,我正在php.net中寻找有关将PHP对象序列化为JSON的信息。 这只是PHP> = 5.4 ,而我正在5.3.x环境中运行。

这种function如何实现PHP <5.4

我还没有用JSON工作太多,但我试图在应用程序中支持API层,并将数据对象( 否则将被发送到视图 )转储到JSON将是完美的。

如果我试图直接序列化对象,它将返回一个空的JSONstring; 这是因为我认为json_encode()不知道与对象做什么。 我应该recursion减less对象到一个数组,然后编码?


 $data = new Mf_Data(); $data->foo->bar['hello'] = 'world'; 

echo json_encode($data)产生一个空对象:

 {} 

然而, var_dump($data)按预期工作:

 object(Mf_Data)#1 (5) { ["_values":"Mf_Data":private]=> array(0) { } ["_children":"Mf_Data":private]=> array(1) { [0]=> array(1) { ["foo"]=> object(Mf_Data)#2 (5) { ["_values":"Mf_Data":private]=> array(0) { } ["_children":"Mf_Data":private]=> array(1) { [0]=> array(1) { ["bar"]=> object(Mf_Data)#3 (5) { ["_values":"Mf_Data":private]=> array(1) { [0]=> array(1) { ["hello"]=> string(5) "world" } } ["_children":"Mf_Data":private]=> array(0) { } ["_parent":"Mf_Data":private]=> *RECURSION* ["_key":"Mf_Data":private]=> string(3) "bar" ["_index":"Mf_Data":private]=> int(0) } } } ["_parent":"Mf_Data":private]=> *RECURSION* ["_key":"Mf_Data":private]=> string(3) "foo" ["_index":"Mf_Data":private]=> int(0) } } } ["_parent":"Mf_Data":private]=> NULL ["_key":"Mf_Data":private]=> NULL ["_index":"Mf_Data":private]=> int(0) } 

附录

1)

所以这是我为Mf_Data类devise的toArray()函数:

 public function toArray() { $array = (array) $this; array_walk_recursive($array, function (&$property) { if ($property instanceof Mf_Data) { $property = $property->toArray(); } }); return $array; } 

但是,由于Mf_Data对象也具有对其父( )对象的引用,因此这会失败,并发生recursion。 虽然当我删除_parent引用时,像魅力工作。

2)

为了跟进,我转换的复杂树节点对象的最终function是:

 // class name - Mf_Data // exlcuded properties - $_parent, $_index public function toArray() { $array = get_object_vars($this); unset($array['_parent'], $array['_index']); array_walk_recursive($array, function (&$property) { if (is_object($property) && method_exists($property, 'toArray')) { $property = $property->toArray(); } }); return $array; } 

3)

我正在跟进,稍微清理一下实施。 为instanceof检查使用接口似乎比method_exists()更清洁( 但是method_exists()不交叉inheritance/实现 )。

使用unset()似乎也有点混乱,似乎逻辑应该被重构为另一种方法。 然而,这个实现并没有复制属性数组( 由于array_diff_key ),所以需要考虑一些事情。

 interface ToMapInterface { function toMap(); function getToMapProperties(); } class Node implements ToMapInterface { private $index; private $parent; private $values = array(); public function toMap() { $array = $this->getToMapProperties(); array_walk_recursive($array, function (&$value) { if ($value instanceof ToMapInterface) { $value = $value->toMap(); } }); return $array; } public function getToMapProperties() { return array_diff_key(get_object_vars($this), array_flip(array( 'index', 'parent' ))); } } 

编辑 :目前是2016-09-24,并且PHP 5.4已经在2012-03-01发布,支持已经 2015-09-01 结束 。 不过,这个答案似乎得到了提升。 如果您仍然使用PHP <5.4,那么您正在创build安全风险并终止您的项目 。 如果你没有足够的理由保持<5.4,甚至已经使用版本> = 5.4, 不要使用这个答案 ,只要使用PHP> = 5.4(或者,你知道最近的一个),并实现JsonSerializable接口


你可以定义一个函数,比如名为getJsonData(); ,这将返回一个数组, stdClass对象或一些其他对象与可见参数,而不是私人/受保护的,并做一个json_encode($data->getJsonData()); 。 本质上,从5.4开始实现这个function,但是手工调用它。

像这样的东西可以工作,因为get_object_vars()从类内部调用,可以访问私有/受保护的variables:

 function getJsonData(){ $var = get_object_vars($this); foreach ($var as &$value) { if (is_object($value) && method_exists($value,'getJsonData')) { $value = $value->getJsonData(); } } return $var; } 

在最简单的情况下,types暗示应该工作:

 $json = json_encode( (array)$object ); 

json_encode()只会编码公共成员variables。 所以如果你想包括私人一旦你必须自己做(如其他人所build议的)

以下代码使用reflection来完成这项工作。 它假定你有你想要序列化的属性的getter

  <?php /** * Serialize a simple PHP object into json * Should be used for POPO that has getter methods for the relevant properties to serialize * A property can be simple or by itself another POPO object * * Class CleanJsonSerializer */ class CleanJsonSerializer { /** * Local cache of a property getters per class - optimize reflection code if the same object appears several times * @var array */ private $classPropertyGetters = array(); /** * @param mixed $object * @return string|false */ public function serialize($object) { return json_encode($this->serializeInternal($object)); } /** * @param $object * @return array */ private function serializeInternal($object) { if (is_array($object)) { $result = $this->serializeArray($object); } elseif (is_object($object)) { $result = $this->serializeObject($object); } else { $result = $object; } return $result; } /** * @param $object * @return \ReflectionClass */ private function getClassPropertyGetters($object) { $className = get_class($object); if (!isset($this->classPropertyGetters[$className])) { $reflector = new \ReflectionClass($className); $properties = $reflector->getProperties(); $getters = array(); foreach ($properties as $property) { $name = $property->getName(); $getter = "get" . ucfirst($name); try { $reflector->getMethod($getter); $getters[$name] = $getter; } catch (\Exception $e) { // if no getter for a specific property - ignore it } } $this->classPropertyGetters[$className] = $getters; } return $this->classPropertyGetters[$className]; } /** * @param $object * @return array */ private function serializeObject($object) { $properties = $this->getClassPropertyGetters($object); $data = array(); foreach ($properties as $name => $property) { $data[$name] = $this->serializeInternal($object->$property()); } return $data; } /** * @param $array * @return array */ private function serializeArray($array) { $result = array(); foreach ($array as $key => $value) { $result[$key] = $this->serializeInternal($value); } return $result; } } 

只需实现一个由PHP JsonSerializable给出的接口。

由于您的对象types是自定义的,我倾向于同意您的解决scheme – 使用编码方法(如JSON或序列化内容)将其分解成更小的片段,另一端具有相应的代码来重构对象。

我的版本:

 json_encode(self::toArray($ob)) 

执行:

 private static function toArray($object) { $reflectionClass = new \ReflectionClass($object); $properties = $reflectionClass->getProperties(); $array = []; foreach ($properties as $property) { $property->setAccessible(true); $value = $property->getValue($object); if (is_object($value)) { $array[$property->getName()] = self::toArray($value); } else { $array[$property->getName()] = $value; } } return $array; } 

JsonUtils: GitHub

我做了一个很好的帮助类,它将get方法转换成一个数组。 它不依赖于属性,只是方法。

所以我有一个包含两个方法的以下审查对象:

评论

  • getAmountReviews:int
  • getReviews:评论数组

评论

  • getSubject
  • getDescription

我写的脚本将它转换成一个数组,其属性如下所示:

  { amount_reviews: 21, reviews: [ { subject: "In een woord top 1!", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque laoreet lacus quis eros venenatis, sed tincidunt mi rhoncus. Aliquam ut pharetra diam, nec lobortis dolor." }, { subject: "En een zwembad 2!", description: "Maecenas et aliquet mi, a interdum mauris. Donec in egestas sem. Sed feugiat commodo maximus. Pellentesque porta consectetur commodo. Duis at finibus urna." }, { subject: "In een woord top 3!", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque laoreet lacus quis eros venenatis, sed tincidunt mi rhoncus. Aliquam ut pharetra diam, nec lobortis dolor." }, { subject: "En een zwembad 4!", description: "Maecenas et aliquet mi, a interdum mauris. Donec in egestas sem. Sed feugiat commodo maximus. Pellentesque porta consectetur commodo. Duis at finibus urna." }, { subject: "In een woord top 5!", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque laoreet lacus quis eros venenatis, sed tincidunt mi rhoncus. Aliquam ut pharetra diam, nec lobortis dolor." } ]} 

来源: PHP的串行器,它将一个对象转换为一个可以编码为JSON的数组。

你所要做的就是将json_encode包装在输出中。

有关脚本的一些信息:

  • 只有以get开头的方法被添加
  • 私有方法被忽略
  • 构造函数被忽略
  • 方法名中的大写字母将被replace为下划线和小写字符

我花了几个小时来解决同样的问题。 我的转换对象包含许多其他的定义,我不应该触摸(API),所以我想出了一个解决scheme,可能会慢我猜,但我用它的开发目的。

这个将任何对象转换为数组

 function objToArr($o) { $s = '<?php class base { public static function __set_state($array) { return $array; } } function __autoload($class) { eval("class $class extends base {}"); } $a = '.var_export($o,true).'; var_export($a); '; $f = './tmp_'.uniqid().'.php'; file_put_contents($f,$s); chmod($f,0755); $r = eval('return '.shell_exec('php -f '.$f).';'); unlink($f); return $r; } 

这将任何对象转换为stdClass

 class base { public static function __set_state($array) { return (object)$array; } } function objToStd($o) { $s = '<?php class base { public static function __set_state($array) { $o = new self; foreach($array as $k => $v) $o->$k = $v; return $o; } } function __autoload($class) { eval("class $class extends base {}"); } $a = '.var_export($o,true).'; var_export($a); '; $f = './tmp_'.uniqid().'.php'; file_put_contents($f,$s); chmod($f,0755); $r = eval('return '.shell_exec('php -f '.$f).';'); unlink($f); return $r; }