Custom JSON De-serializer for PHP
My previous post dealt with taking an object graph and turning it into JSON. If you are building a RESTful web service using PHP and you don’t have the option of using frameworks like Jersey for Java or just prefer doing things in PHP then you also might find the following de-serializer useful. It is not complete in its current form but I chose to share it as is in case someone needed an idea of how to accomplish this.
<?php namespace YourCompanyUtil;
/**
*
* Custom JSON de-serializer using reflection.
* WARNING: NOT TO BE USED FOR HI LOAD APPLICATIONS!!!
*
* @author christian [a] binaryworx.net
*
*/
class JsonDeserializer{
private $aSimpleTypes = array( 'bool', 'boolean', 'int', 'integer', 'float', 'double', 'string', 'array');
/**
*
* depth
* @var integer
*/
private $iDepth = 6;
/**
*
* keep track of the current depth
* @var integer
*/
private $iCurrentDepth = 0;
/**
*
* are we using type prefixes
* @var boolean
*/
private $bUsesTypePrefixes = true;
/**
*
* default constructor with options
* @param array $aOpts
*/
public function __construct(array $aOpts = array()){
}
/**
*
* unmarshall json string to given object type
* @param string $sType
* @param string $sJson
* @return
*/
public function unMarshall($sType, $sJson){
$aObjectGraph = json_decode($sJson, true);
return $this->_unMarshall($aObjectGraph, $sType);
}
/**
*
* do the actual unmarshalling...
* @param array $aObjectGraph
* @param string $sType
*/
protected function _unMarshall(array $aObjectGraph, $sType){
$oRC = new ReflectionClass($sType);
$aProperties = $oRC->getProperties();
$o = $oRC->newInstance();
foreach($aProperties as $oRPC){
$sName = ($this->bUsesTypePrefixes) ? lcfirst(substr($oRPC->getName(), 1)) : $oRPC->getName();
if(isset($aObjectGraph[$sName])){
if(!$oRPC->isPublic()){
$oRPC->setAccessible(true);
}
if($sType = $this->_getTypeFromDocBlock($oRPC)){
$bScalar = is_scalar($aObjectGraph[$sName]);
if($bScalar && $this->_isSimpleType($sType)){
$oRPC->setValue($o, $aObjectGraph[$sName]);
}elseif($bScalar){
$oRPC->setValue($o, $this->_objectForType($sType, $aObjectGraph[$sName]));
}else{
if($this->_hasGenerics($sType)){
// handle generics here if necessary...
}else{
$oRPC->setValue($o, $this->_unMarshall($aObjectGraph[$sName], $sType));
}
}
}else{
throw new Exception('Missing type annotation for property: ' . $sName);
}
if(!$oRPC->isPublic()){
$oRPC->setAccessible(false);
}
}
}
return $o;
}
/**
*
* fetch the varialble type from the docblock
* @param ReflectionProperty $oRPC
*/
protected function _getTypeFromDocBlock(ReflectionProperty $oRPC){
$sComment = $oRPC->getDocComment();
$aTmp = array();
if(preg_match_all('|@vars([\\a-z_]{1}[\\a-z0-9_<>]+)s|i', $sComment, $aTmp, PREG_SET_ORDER)){
return $aTmp[0][1];
}
return null;
}
/**
*
* determine if type has generics
* e.g. type could be array or YourCompanyUtilMyCollectionClass
*
* @param string $sType
* @return boolean
*/
protected function _hasGenerics($sType){
return preg_match('|(<([\\a-z0-9_]+)>)|i', $sType);
}
/**
*
* check to see if the type is actually a simple type...
* @param string $sType
* @return boolean
*/
protected function _isSimpleType($sType){
return in_array($sType, $this->aSimpleTypes);
}
/**
*
* factory for special object types
* this method can loop through registered type handlers... yet to be implemented
* @param string $sType
* @param mixed $mValue
* @return object
*/
protected function _objectForType($sType, $mValue){
return null;
}
}