/**
  * Recursively deserialize data for the given node.
  *
  * @param   xml.Node node
  * @return  var
  * @throws  lang.IllegalArgumentException if the data cannot be deserialized
  * @throws  lang.ClassNotFoundException in case a XP object's class could not be loaded
  * @throws  xml.XMLFormatException
  */
 protected function _unmarshall(Node $node)
 {
     // Simple form: If no subnode indicating the type exists, the type
     // is string, e.g. <value>Test</value>
     if (!$node->hasChildren()) {
         return (string) $node->getContent();
     }
     // Long form - with subnode, the type is derived from the node's name,
     // e.g. <value><string>Test</string></value>.
     $c = $node->nodeAt(0);
     switch ($c->getName()) {
         case 'struct':
             $ret = [];
             foreach ($c->getChildren() as $child) {
                 $data = [];
                 $data[$child->nodeAt(0)->getName()] = $child->nodeAt(0);
                 $data[$child->nodeAt(1)->getName()] = $child->nodeAt(1);
                 $ret[$data['name']->getContent()] = $this->_unmarshall($data['value']);
                 unset($data);
             }
             if (!isset($ret['__xp_class'])) {
                 return $ret;
             }
             $class = XPClass::forName($ret['__xp_class']);
             $instance = $class->newInstance();
             foreach ($ret as $name => $value) {
                 if (!$class->hasField($name)) {
                     continue;
                 }
                 $field = $class->getField($name);
                 if ($field->getModifiers() & MODIFIER_STATIC) {
                     continue;
                 }
                 $class->getField($name)->setAccessible(true)->set($instance, $value);
             }
             return $instance;
         case 'array':
             $ret = [];
             foreach ($c->nodeAt(0)->getChildren() as $child) {
                 $ret[] = $this->_unmarshall($child);
             }
             return $ret;
         case 'int':
         case 'i4':
             return (int) $c->getContent();
         case 'double':
             return (double) $c->getContent();
         case 'boolean':
             return (bool) $c->getContent();
         case 'string':
             return (string) $c->getContent();
         case 'dateTime.iso8601':
             return new Date($c->getContent());
         case 'nil':
             return null;
         case 'base64':
             return new Bytes(base64_decode($c->getContent()));
         default:
             throw new \lang\IllegalArgumentException('Could not decode node as its type is not supported: ' . $c->getName());
     }
 }