我需要一个递归的php函数来循环遍历xml文件

时间:2017-12-13 16:11:32

标签: php xml recursion simplexml

我试图遍历xml文件并将节省了它的值保存到数组中(key => value)。我还希望它跟踪它传递的节点(类似于数组(users_user_name =>" myName",users_user_email =>" myEmail")等)。

我知道怎么做但是有问题。所有的节点都可以有孩子,那些孩子也可能有孩子等。所以我需要某种递归功能来保持孩子们的循环直到它到达最后一个孩子。

到目前为止,我得到了这个:

//loads the xml file and creates simpleXML object
        $xml = simplexml_load_string($content);

        // for each root value
        foreach ($xml->children() as $children) {
            // for each child of the root node
            $node = $children;
            while ($children->children()) {
                foreach ($children as $child) {

                    if($child->children()){
                        break;
                    }
                    $children = $node->getName();
                    //Give key a name
                    $keyOfValue = $xml->getName() . "_" . $children . "_" . $child->getName();
                    // pass value from child to children
                    $children = $child;

                    // no children, fill array: key => value
                    if ($child->children() == false) {
                        $parent[$keyOfValue] = (string)$child;
                    }
                }
            }
            $dataObject[] = $parent;
        }

"休息;"是为了防止它给我错误的价值因为"孩子"是一个对象,而不是最后一个孩子。

4 个答案:

答案 0 :(得分:0)

你想要使用递归!

这是一个简单的递归示例:

function doThing($param) {
    // Do what you need to do
    $param = alterParam($param);
    // If there's more to do, do it again
    if ($param != $condition) {
        $param = doThing($param);
    }
    // Otherwise, we are ready to return the result
    else {
        return $param;
    }
}

您可以将此思维应用于您的特定用例。

答案 1 :(得分:0)

使用递归,你可以写一些复杂的'处理,但问题是失去你的位置。

我在这里使用的函数传递了一些东西来跟踪名称和当前输出,以及它当前正在使用的节点。如您所见 - 该方法检查是否有任何子节点并再次调用该函数来处理它们中的每一个。

$content = <<< XML
<users>
    <user>
        <name>myName</name>
        <email>myEmail</email>
        <address><line1>address1</line1><line2>address2</line2></address>
    </user>
</users>
XML;

function processNode ( $base, SimpleXMLElement $node, &$output )  {
    $base[] = $node->getName();
    $nodeName = implode("_", $base);
    $childNodes = $node->children();
    if ( count($childNodes) == 0 )  {
        $output[ $nodeName ] = (string)$node;
    }
    else    {
        foreach ( $childNodes as $newNode ) {
            processNode($base, $newNode, $output);
        }
    }
}

$xml = simplexml_load_string($content);
$output = [];
processNode([], $xml, $output);
print_r($output);

打印出来......

Array
(
    [users_user_name] => myName
    [users_user_email] => myEmail
    [users_user_address_line1] => address1
    [users_user_address_line2] => address2
)

通过这种实现,内容存在局限性 - 例如 - 重复内容只会保留最后一个值(例如,有多个用户)。

答案 2 :(得分:0)

//Using SimpleXML library 


// Parses XML but returns an Object for child nodes

public function getNodes($root) 
{   
    $output = array();

    if($root->children()) {
        $children = $root->children();   

        foreach($children as $child) {
            if(!($child->children())) {
                $output[] = (array) $child;
            }
            else {
                $output[] = self::getNodes($child->children());
            } 
        }
    }   
    else {
        $output = (array) $root;
    }   

    return $output;
}   

答案 3 :(得分:0)

我将添加到此 当名称空间出现问题时,我遇到了一些麻烦,所以我做了以下递归函数来解决节点

此方法进入最深的节点并将其用作值,在我的情况下,顶部节点的nodeValue包含嵌套的所有值,因此我们必须深入研究最低层并将其用作真实值

    // using the XMLReader to read an xml file ( in my case it was a 80gig xml file which is why i don't just load everything into memory )
    $reader = new \XMLReader;
    $reader->open($path); // where $path is the file path to the xml file
    
    // using a dirty trick to skip most of the xml that is irrelevant where $nodeName is the node im looking for
    // then in the next while loop i skip to the next node
    while ($reader->read() && $reader->name !== $nodeName);
    while ($reader->name === $nodeName) {
        $doc = new \DOMDocument;
        $dom = $doc->importNode($reader->expand(), true);
        $data = $this->processDom($dom);
        $reader->next($dom->localName);
    }
   
    public function processDom(\DOMNode $node)
    {
        $data = [];
        /** @var \DomNode $childNode */
        foreach ($node->childNodes as $childNode) {
            // child nodes include of a lot of #text nodes which are irrelevant for me, so i just skip them
            if ($childNode->nodeName === '#text') {
                continue;
            }
            $childData = $this->processDom($childNode);
            if ($childData === null || $childData === []) {
                $data[$childNode->localName] = $childNode->nodeValue;
            } else {
                $data[$childNode->localName] = $childData;
            }
        }
        return $data;
    }