Custom JSON Serializer for PHP
The project I am working on currently requires serializing PHP objects into natural JSON.
After a while it gets a bit old having to transform those objects into key-value pair arrays to then transform them into JSON using json_encode. An alternative would be to make all properties public and then just serialize the objects using json_encode… so much for encapsulation though.
Having worked with Java REST frameworks like Jersey I am used to having plug-able and configurable serializers like JAXB and Jackson. These libraries use intelligent defaults so the amount of configuration and/or annotation is nil or an absolute minimum.
However, as the choice of technology fell on PHP for almost all tiers in the current project I had to come up with an alternative solution producing JSON.
Using F3, a light weight PHP framework for serving up the RESTful JSON endpoints all that was missing was the (fairly) intelligent serializer.
The light weight implementation below is roughly what I used on the project. You might have to tweak things here and there to suit your projects serialization (marshaling) needs.
<?php namespace YourCompanyUtil;
/**
*
* Custom JSON serializer using reflection.
* WARNING: NOT TO BE USED FOR HIGH LOAD APPLICATIONS!!!
*
* @author christian [a] binaryworx.net
*
*/
class JsonSerializer{
/**
*
* maximum depth of serialization
* @var integer
*/
private $iDepth = 6;
/**
*
* keep track of the current depth
* @var integer
*/
private $iCurrentDepth = 0;
/**
*
* are we using type prefixes (e.g. bBoolean, sString, fFloat or just _underScore for private members)
* @var boolean
*/
private $bUsesTypePrefixes = true;
/**
*
* default constructor with options
* @param array $aOpts
*/
public function __construct(array $aOpts = array()){
}
/**
*
* serialize object/graph to json string
* @param object $object
* @return string
*/
public function marshall($object){
$aObjectGraph = $this->_marshall($object);
return json_encode($aObjectGraph);
}
/**
*
* do the actual serialization and return array of object graph
* @param object $object
* @return array
*/
private function _marshall($object){
if($this->iCurrentDepth >= $this->iDepth){
return array();
}
$this->iCurrentDepth++;
$aPartialGraph = array();
if($object instanceof ArrayAccess){
$aPartialGraph = $this->_marshallCollection($object);
}else{
$oR = new ReflectionClass($object);
foreach($oR->getProperties() as $oProperty){
if(!strstr($oProperty->getDocComment(), '@Transient')){
$aPartialGraph = array_merge($aPartialGraph, $this->_marshalProperty($oProperty, $object));
}
}
}
$this->iCurrentDepth--;
return $aPartialGraph;
}
/**
*
* manage individual properties
*
* @param ReflectionProperty $oProperty
* @param object $object
* @return array
*/
private function _marshalProperty(ReflectionProperty $oProperty, $object){
$sPropName = $oProperty->getName();
if($this->bUsesTypePrefixes){
$sPropName = lcfirst(substr($sPropName, 1));
}
$aProperty = array();
$mValue = null;
if($oProperty->isPrivate() || $oProperty->isProtected()){
$oProperty->setAccessible(true);
$mValue = $oProperty->getValue($object);
$oProperty->setAccessible(false);
}else{
$mValue = $oProperty->getValue($object);
}
switch (gettype($mValue)){
case 'array':
$aProperty[$sPropName] = $this->_marshallArray($mValue);
break;
case 'object':
$aProperty[$sPropName] = $this->_marshall($mValue);
break;
default:
$aProperty[$sPropName] = $mValue;
}
return $aProperty;
}
/**
*
* marshall array
* @param array $aValues
* @return array
*/
private function _marshallArray(array $aValues){
$aReturn = array();
foreach($aValues as $mKey=>$mValue){
if(is_object($mValue)){
$aReturn[$mKey] = $this->_marshall($mValue);
}elseif(is_array($mValue)){
$aReturn[$mKey] = $this->_marshallArray($mValue);
}else{
$aReturn[$mKey] = $mValue;
}
}
return $aReturn;
}
/**
*
* marshall collection
* @param ArrayAccess $collection
*/
private function _marshallCollection(ArrayAccess $collection){
$aReturn = array();
foreach($collection as $mValue){
$aReturn[] = $this->_marshall($mValue);
}
return $aReturn;
}
}
Feel free to use this code “as is” and share any improvements with the rest of us. I hope this helps some of you having similar problems to solve.