PHP object serialization for JSON

So, I wandered around php.net for information on serializing PHP objects in JSON when I came across the new JsonSerializable Interface . This is only PHP> = 5.4 , but I work in the 5.3.x environment.

How is this PHP functionality achieved <5.4

I haven't worked much with JSON yet, but I'm trying to maintain the API level in the application, and dumping a data object (which would otherwise be sent to the view) into JSON would be ideal.

If I try to serialize the object directly, it returns an empty JSON string; because I assume json_encode() does not know what to do with the object. Should I recursively dump an object into an array and then encode it?




Example

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

echo json_encode($data) creates an empty object:

 {} 

var_dump($data) works as expected:

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



Adding

one)

So this is the toArray() function that I developed for the Mf_Data class:

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

However, since Mf_Data objects also have a reference to their parent (containing) object, this fails with recursion. Works like a charm, but when I remove the _parent link.

2)

Just to keep an eye on, the final function to convert the complex tree-node object I went with was:

 // 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)

I come again, a little cleaner implementation. Using interfaces to validate instanceof seems a lot cleaner than method_exists() (however method_exists() does cross-inherit / implementation).

Using unset() seemed a little dirty, and it seems that the logic should be reorganized into another method. However, this implementation copies the array of properties (due to array_diff_key ), so something needs to be considered.

 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' ))); } } 
+99
json object php object-serialization
Jul 26 2018-11-21T00:
source share
9 answers



edit : it is currently 2016-09-24, and PHP 5.4 was released on 2012-03-01, and support was completed on 2015-09-01. However, this answer seems to be gaining momentum. If you are still using PHP <5.4, you pose a security risk and jeopardize your project. If you have no good reason 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 must define a function, for example, named getJsonData(); , which will return either an array, a stdClass object, or some other object with visible parameters, not private / protected, and will make json_encode($data->getJsonData()); . In essence, implement the function from 5.4, but name it manually.

Something like this will work, since get_object_vars() is called from within 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; } 
+44
Jul 26 2018-11-21T00:
source share

In the simplest cases, the hinting type should work:

 $json = json_encode( (array)$object ); 
+90
Jul 26 2018-11-21T00:
source share

json_encode() will only encode public member variables. therefore, if you want to enable private, as soon as you do it yourself (as others suggested)

+19
Jul 26 2018-11-21T00:
source share

The following code does the work using reflection. It is assumed that you have recipients for the properties that 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; } } 
+8
Aug 28 '14 at 6:48
source share

Just implement the interface defined by PHP JsonSerializable .

+5
Jan 25 '16 at 14:08
source share

Since your object type is ordinary, I would like to agree with your decision to split it into smaller segments using an encoding method (for example, JSON or serializing content), and at the other end the corresponding code will rebuild the object.

+2
Jul 26 2018-11-21T00:
source share

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

+2
Jun 04 '17 at 9:43 on
source share

I made a nice helper class that converts an object with get methods into an array. It does not rely on properties, just methods.

So, I have the following overview object, which contains two methods:

Overview

  • getAmountReviews: int
  • getReviews: an array of comments

Comment

  • getSubject
  • getDescription

The script I wrote converts it into an array with properties that 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 into an array that can be encoded in JSON.

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

Some script info:

  • Only methods starting with get are added.
  • Private methods are ignored
  • Constructor is ignored
  • The capital signs in the method name will be replaced by an underscore and a lower symbol
0
Jun 11 '16 at 21:38
source share

I spent several hours on the same problem. My conversion object contains many others whose definitions I should not concern (API), so I came up with a solution that can be slow, I think, but I use it for development purposes.

This will convert any object to an 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 will convert 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; } 
-7
Aug 15 '12 at 17:08
source share



All Articles