protected function nodeToArray($node)
    {
        $output = array();
        switch ($node->nodeType) {
            case XML_CDATA_SECTION_NODE:
            case XML_TEXT_NODE:
                $output = trim($node->textContent);
                break;
            case XML_ELEMENT_NODE:
                for ($i = 0, $m = $node->childNodes->length; $i < $m; $i++) {
                    $child = $node->childNodes->item($i);
                    $value = $this->nodeToArray($child);
                    if (isset($child->tagName)) {
                        $tag = $child->tagName;
                        if (!isset($output[$tag])) {
                            $output[$tag] = array();
                        }
                        $output[$tag][] = $value;
                    } elseif ($value || $value === '0') {
                        $output = (string) $value;
                    }
                }

                // Edge case of a node containing a text node, which also has
                // attributes. this way we'll retain text and attributes for
                // this node.
                if (is_string($output) && $node->attributes->length) {
                    $output = array('@text' => $output);
                }

                if (is_array($output)) {
                    if ($node->attributes->length) {
                        $attributes = array();
                        foreach ($node->attributes as $attrName => $attrNode) {
                            $attributes[$attrName] = (string) $attrNode->value;
                        }
                        $output['@attributes'] = $attributes;
                    }
                    foreach ($output as $tag => $value) {
                        if (is_array($value) && count($value) == 1 && $tag != '@attributes') {
                            $output[$tag] = $value[0];
                        }
                    }
                }
                break;
        }

        return $output;
    }