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.kassab@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.

This entry was posted in PHP and tagged , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

*


four + 8 =

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>