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-&gt;_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-&gt;iCurrentDepth &gt;= $this-&gt;iDepth){
			return array();
		}
		$this-&gt;iCurrentDepth++;
		$aPartialGraph = array();
		if($object instanceof ArrayAccess){
			$aPartialGraph = $this-&gt;_marshallCollection($object);
		}else{
			$oR = new ReflectionClass($object);
			foreach($oR-&gt;getProperties() as $oProperty){
				if(!strstr($oProperty-&gt;getDocComment(), '@Transient')){
					$aPartialGraph = array_merge($aPartialGraph, $this-&gt;_marshalProperty($oProperty, $object));
				}
			}
		}
 
		$this-&gt;iCurrentDepth--;
		return $aPartialGraph;
	}
 
	/**
	 *
	 * manage individual properties
	 *
	 * @param ReflectionProperty $oProperty
	 * @param object $object
	 * @return array
	 */
	private function _marshalProperty(ReflectionProperty $oProperty, $object){
		$sPropName = $oProperty-&gt;getName();
		if($this-&gt;bUsesTypePrefixes){
			$sPropName = lcfirst(substr($sPropName, 1));
		}
 
		$aProperty = array();
		$mValue = null;
		if($oProperty-&gt;isPrivate() || $oProperty-&gt;isProtected()){
			$oProperty-&gt;setAccessible(true);
			$mValue = $oProperty-&gt;getValue($object);
			$oProperty-&gt;setAccessible(false);
		}else{
			$mValue = $oProperty-&gt;getValue($object);
		}
 
		switch (gettype($mValue)){
			case 'array':
				$aProperty[$sPropName] = $this-&gt;_marshallArray($mValue);
				break;
			case 'object':
				$aProperty[$sPropName] = $this-&gt;_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=&gt;$mValue){
			if(is_object($mValue)){
				$aReturn[$mKey] = $this-&gt;_marshall($mValue);
			}elseif(is_array($mValue)){
				$aReturn[$mKey] = $this-&gt;_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-&gt;_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.

Show Comments