PHP与XML。使用PHP循环遍历XML文件

3

我现在深陷于foreach的炼狱中,试图想出一种使用PHP遍历这个XML文件(如下所示)的方法。我想要做的是:

  1. 获取所有文件夹元素的名称
  2. 如果文件夹元素具有子文件夹属性为yes,则向下移动一个级别并获取该文件夹元素的名称
  3. 否则继续下一个文件夹元素

gallerylist.xml:

<?xml version="1.0" encoding="ISO-8859-1"?>
<gallerylisting exists="yes">
<folder subfolder="yes">
Events
   <folder subfolder="yes">
   Beach_Clean_2010
        <folder subfolder="no">
        Onna_Village
        </folder>
            <folder subfolder="no">
            Sunabe_Sea_Wall
        </folder>
        </folder>
  </folder>
  <folder subfolder="no">
  Food_And_Drink
  </folder>
  <folder subfolder="no">
  Inside
  </folder>
  <folder subfolder="no">
  Location
  </folder>
  <folder subfolder="no">
  NightLife
  </folder>
</gallerylisting>

gallerylisting.php

<?php
$xmlref = simplexml_load_file("gallerylisting.xml");
foreach($xmlref->children() as $child) {
    foreach($child->attributes() as $attr => $attrVal) {
        print $child;
        if($attrVal == "yes") {
            foreach($child->children() as $child) {
                echo $child;
                foreach($child->attributes() as $attr => $attrVal) {
                    if($attrVal == "yes") {
                        foreach($child->children() as $child) {
                            echo $child;
                        }
                    }                   
                }
            }
        }
    }
}

我现在写的PHP脚本已经使用了五个foreach循环,但我并不喜欢这种写法。而且,如果我的文件夹中再有一个子文件夹,我就得再添加同样的循环。

$if(attrVal=="yes")...etc.

再次进入,但是...不行!有没有任何方法可以避免这种情况。我对PHP和尤其是PHP和XML都很陌生。

感谢任何帮助。


由于您将此问题标记为效率,我会提出这一点。您的xml中不需要“subfolder”属性。PHP库将帮助您找出节点是否具有子节点。如果它们有,请递归使用@Matt的函数。而且,您不应该混合“文本”元素(例如文件夹名称)和节点。那看起来有点糟糕。尝试使用“名称”属性。 - Frank
为将来参考记下了!感谢坦率相待!(badump ch!) - SomeShinyObject
3个回答

5

递归在这里可能对您有益。

<?php

function display_entities( $xml )
{
    foreach($xml->children() as $child) {
        foreach($child->attributes() as $attr => $attrVal) {
            print $child;
            if($attrVal == "yes") {
              display_entities( $child->children() );
            }
        }
    }
}

$xmlref = simplexml_load_file("gallerylisting.xml");

display_entities($xmlref->children());

我得试一下你提到的交易和Wrikken在下面谈论的xpath,然后再给出我的答案。不过这似乎是我用JavaScript做的事情,所以有点超前。 - SomeShinyObject
耶!耶!这就是了,兄弟,完全就是这样。虽然我在 display_entities 参数中做了一些小改动,但实际调用时只使用了 $xmlref。这让我得到了所有的东西!你太牛了,兄弟。 - SomeShinyObject
1
你应该使用一个标志并将其移出内部的foreach循环,否则内部的display_entities可能会被多次调用,而每个子元素只需要调用一次。 - faham

2

使用XPath:

如果在末级目录中 subfolder=no 不可靠(例如可能没有总是设置为'no'):

foreach($xmlref->xpath('//folder[not(@subfolder) or @subfolder!="yes"]') as $node){

如果它是:
foreach($xmlref->xpath('//folder[@subfolder="no"]') as $node){

或者,如果您想检查没有文件夹子项的文件夹,而不考虑属性:

foreach($xmlref->xpath('//folder[not(folder)]') as $node){

我得研究一下xpath的问题。看起来相当合法。 - SomeShinyObject

0
你可以尝试使用xpath代替嵌套循环的方法。
运行查询。
gallerylisting//folder[@subfolder="yes"]/text() 

在您提供的XML文档中,使用{{link1:此XPath测试工具}}可以得到“Events”和“Beach_Clean_2010”的结果,这正是我认为您想要的。该查询将查找根节点gallerylisting下的所有文件夹元素,如果它们具有子文件夹属性等于“yes”,则将返回节点中的文本。

因此,在PHP中,我们有

<?php
    $xmldoc = new DOMDocument();
    $xmldoc->load('gallerylisting.xml');

    $xpathvar = new Domxpath($xmldoc);

    $queryResult = $xpathvar->query('gallerylisting//folder[@subfolder="yes"]/text()');
    foreach($queryResult as $result){
            echo $result->textContent;
    }
?>

我不是 PHP 专家,所以我相信 这个 StackOverflow 问题 中的代码是可行的。需要注意的一点是,在 XPath 中使用 //name 表示查找所有与该名称匹配的子孙节点。在大型文档中,这可能非常慢。

It seems there is multiple ways to process xpath with PHP. An example from this page does the query like so:

   $result = $xml->xpath("gallerylisting//folder[@subfolder="yes"]/text()");

   print_r($result);
?> 


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接