static public function fromArray(array $inArray, callable $inOptDeserializer=null, $inOptDataTag='data', $inOptChildrenTag='children') {

        if (is_null($inOptDeserializer)) {
            $inOptDeserializer = function($inSerialised) {
                return $inSerialised;
            };
        }

        self::$__dataTag = $inOptDataTag;
        self::$__childrenTag = $inOptChildrenTag;

        $result = [ 'tree' => new Tree(), 'index' => [], 'id' => 0 ];

        $builder = function (array &$inCurrentNode, $isLeaf, &$inParentNode, array &$inOutData) use ($inOptDeserializer, $inOptDataTag, $inOptChildrenTag) {

            if (! array_key_exists($inOptDataTag, $inCurrentNode)) {
                throw new \Exception("The given array is not valid. No data defined for the current node!");
            }

            if (! array_key_exists($inOptChildrenTag, $inCurrentNode)) {
                throw new \Exception("The given array is not valid. No children defined for the current node!");
            }

            if (! is_array($inCurrentNode[$inOptChildrenTag])) {
                throw new \Exception("This given array in not a valid tree. Key $inOptChildrenTag is not an array!");
            }

            // Generate a unique ID for the current node, so it can be indexed.
            $currentNodeId = $inOutData['id'];
            $inOutData['id'] += 1;
            $inCurrentNode['id'] = $currentNodeId; // Add the ID to the array that represents the current node.

            // Special processing for the root node.
            if (is_null($inParentNode)) {
                /** @var Tree $tree */
                $tree = $inOutData['tree']; // Create this variable so we have the auto completion and the API checks.
                $root = new Node($inOptDeserializer($inCurrentNode[$inOptDataTag]));
                $tree->setRoot($root);
                $inOutData['index'][$currentNodeId] = $root;
                return;
            }

            // Note that the "parent node" has already been processed by this function.
            // Therefore, it has been assigned an ID. And the ID has been indexed.
            /** @var Node $parent */
            $parent = $inOutData['index'][$inParentNode['id']]; // Create this variable so we have the auto completion and the API checks.
            $newNode = $parent->addChild($inOptDeserializer($inCurrentNode[$inOptDataTag]));
            $inOutData['index'][$currentNodeId] = $newNode;
        };

        self::arrayTraverse($inArray, $builder, $result);
        return $result['tree'];
    }