将 PHP 对象序列化为 JSON

因此,当我偶然发现新的 可串行化接口时,我正在 Php.net中寻找关于将 PHP 对象序列化为 JSON 的信息。不过它只是 PHP > = 5.4,而且我是在一个5.3. x 环境中运行的。

这种功能是如何实现 PHP < 5.4的?

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

如果我试图直接序列化该对象,它将返回一个空 JSON 字符串; 这是因为我假设 json_encode()不知道该如何处理该对象。我是否应该递归地将对象减少到一个数组中,然后编码 那个


例子

$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类设计的 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对象也有对其父对象(包含)的引用,因此递归会失败。当我删除 _parent引用时,它就像一个魔咒。

2)

接下来,转换一个复杂的树节点对象的最后一个函数是:

// 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()是否横切继承/实现)干净得多。

使用 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'
)));
}


}
168002 次浏览

Since your object type is custom, I would tend to agree with your solution - break it down into smaller segments using an encoding method (like JSON or serializing the content), and on the other end have corresponding code to re-construct the object.


edit: it's currently 2016-09-24, and PHP 5.4 has been released 2012-03-01, and support has ended 2015-09-01. Still, this answer seems to gain upvotes. If you're still using PHP < 5.4, your are creating a security risk and endagering your project. If you have no compelling reasons to stay at <5.4, or even already use version >= 5.4, do not use this answer, and just use PHP>= 5.4 (or, you know, a recent one) and implement the JsonSerializable interface


You would define a function, for instance named getJsonData();, which would return either an array, stdClass object, or some other object with visible parameters rather then private/protected ones, and do a json_encode($data->getJsonData());. In essence, implement the function from 5.4, but call it by hand.

Something like this would work, as get_object_vars() is called from inside the class, having access to private/protected 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;
}

json_encode() will only encode public member variables. so if you want to include the private once you have to do it by yourself (as the others suggested)

In the simplest cases type hinting should work:

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

I spent some hours on the same problem. My object to convert contains many others whose definitions I'm not supposed to touch (API), so I've came up with a solution which could be slow I guess, but I'm using it for development purposes.

This one converts any object to array

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

This converts any object to 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;
}

Following code is doing the job using reflection. It assumes you have getters for the properties you want to serialize

    <?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;
}
}

Just implement an Interface given by PHP JsonSerializable.

I made a nice helper class which converts an object with get methods to an array. It doesn't rely on properties, just methods.

So i have a the following review object which contain two methods:

Review

  • getAmountReviews : int
  • getReviews : array of comments

Comment

  • getSubject
  • getDescription

The script I wrote will transform it into an array with properties what looks like this:

    {
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."
}
]}

Source: PHP Serializer which converts an object to an array that can be encoded to JSON.

All you have to do is wrap json_encode around the output.

Some information about the script:

  • Only methods which starts with get are added
  • Private methods are ignored
  • Constructor is ignored
  • Capital characters in the method name will be replaced with an underscore and lowercased character

My version:

json_encode(self::toArray($ob))

Implementation:

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

Try using this, this worked fine for me.

json_encode(unserialize(serialize($array)));

Change to your variable types private to public

This is simple and more readable.

For example

Not Working;

class A{
private $var1="valuevar1";
private $var2="valuevar2";
public function tojson(){
return json_encode($this)
}
}

It is Working;

class A{
public $var1="valuevar1";
public $var2="valuevar2";
public function tojson(){
return json_encode($this)
}
}