【问题标题】:Select all nodes with a certain name by using PHP SimpleXML - source code attached使用 PHP SimpleXML 选择具有特定名称的所有节点 - 附上源代码
【发布时间】:2013-05-02 15:12:51
【问题描述】:

在以下 XML 文件中,我尝试打印所有 TestItem 节点,但只获取 4 个 outer 节点。

请问有人知道,如何打印具有该名称的每个节点,无论它们的位置如何?

data.xml:

<?xml version="1.0"?>
<Tests>
    <TestItem Name="UpdateBootProfile" Result="PASS" />
    <TestItem Name="NRB Boot" Result="PASS">
      <TestItem Name="Boot Test" Result="PASS">
        <TestItem Name="PreparePowerSupply" Result="PASS" />
        <TestItem Name="ApplyBatteryVoltage" Result="PASS" />
        <TestItem Name="Shelf Mode test" Result="PASS">
        </TestItem>
        <TestItem Name="ApplyUSBVoltage" Result="PASS" />
        <TestItem Name="DetectBoard" Result="PASS" />
        <TestItem Name="Device Current Profile" Result="PASS" />
        <TestItem Name="Device Connection" Result="PASS">
        </TestItem>
      </TestItem>
    </TestItem>
    <TestItem Name="Check device Type" Result="PASS" />
    <TestItem Name="Assign BSN and Erase EFS" Result="PASS">
    </TestItem>
</Tests>

parse.php:

<?php
        $tmp = 'data.xml';
        $str = file_get_contents($tmp);
        $xml = new SimpleXMLElement($str);
        $items = $xml->xpath('TestItem');

        while(list( , $test) = each($items)) {
                printf("%s %s\n", $test['Name'], $test['Result']);
        }
?>

php -f parse.php 输出(为什么只列出4个TestItem?):

UpdateBootProfile PASS
NRB Boot PASS
Check device Type PASS
Assign BSN and Erase EFS PASS

在 CentOS 6.3 命令行上使用 PHP 5.3.5。

更新:

建议的//TestItem 适用于我上面的简单测试用例,谢谢。

但我的真实数据仍然失败(我无法在此处粘贴):

# grep -w TestItem my_real_file_May_2013_09_35_38.xml |wc -l
143

# php -f parse.php |wc -l
86

有人想知道//TestItem会错过一些节点吗?

更新 2:

确实有效!由于一些 &lt;/TestItem&gt; 结束标签,上面的 grep 突击队计算了更多行 :-)

【问题讨论】:

  • 因为像&lt;TestItem&gt;'. You have parent element `这样的父子元素没有区别,&lt;TestItem&gt;元素也一样。这不应该在那里。
  • 将xml粘贴到pastebin并分享链接

标签: php xpath simplexml


【解决方案1】:

你可以这样做

$testitems = simplexml_load_file("testitem.xml");
if(count($testitems)):
    $result = $testitems->xpath("//TestItem");

    //echo "<pre>";print_r($result);die;
    foreach ($result as $item):
        echo "Name ".$item['Name'] ." and result ". $item['Result'];
        echo "<hr>";
    endforeach;
endif;

通过以上操作,您将获得所有具有&lt;TestItem&gt; 元素的元素。

【讨论】:

  • 谢谢,//TestItem 适用于我的简单示例,但不适用于我的真实文件,我可能不会在此处粘贴。你知道为什么/什么时候//TestItem 会省略一些节点吗?我已经更新了我的问题
  • @AlexanderFarber 看看我为适合您的提议而编辑的答案。
  • @AlexanderFarber: //TestItem 应该可以工作并给出所有匹配的元素。您能否只提供一份复杂文件的样本?让我有更好的想法。
  • 其实你是对的!谢谢你。我被&lt;/TestItem&gt; 标签弄糊涂了(grep 也算在内)
【解决方案2】:

使用以下 xpath 选择节点,无论它们在树中的位置如何:

$items = $xml->xpath('//TestItem');

或者

$items = $xml->xpath('//TestItem/TestItem');

如果你只需要叶子节点。

输出(来自第二个):

UpdateBootProfile PASS
NRB Boot PASS
Boot Test PASS
PreparePowerSupply PASS
ApplyBatteryVoltage PASS
Shelf Mode test PASS
ApplyUSBVoltage PASS
DetectBoard PASS
Device Current Profile PASS
Device Connection PASS
Check device Type PASS
Assign BSN and Erase EFS PASS

注意//。在W3schools XPath tutorial.了解更多信息

【讨论】:

  • 你被because of the w3schools link否决了,所以我建议你用this Wikipedia link替换它...
  • @Passerby 谢谢。但是有人(Guerra)怎么能告诉我我必须链接什么而不是什么?我已经链接了一个完美的教程并提出了正确的表达方式
  • @hek2mgl “w3schools 糟透了”是 SO 中常见的战争口号之一......有点像在 PHP 中使用 mysql_* :) 尽可能避免它。
  • @Passerby 谢谢你的提示 ;).. 将在接下来的帖子中考虑到这一点。 (这个我不会更新,因为我认为否决票是粗鲁和冒犯的).. 但是,再次感谢;)
  • @Guerra 你能指出该教程中的一些错误或不足的信息吗?
【解决方案3】:
function printxml($xml,$deep = 4){
if($xml instanceOf SimpleXMLElement)
    $xml = (array)$xml;
    foreach($xml->TestItem as $t){
        if(is_array($t) && $deep > 0)
            printxml($t, $deep-1);
        else
            echo $t['Name'].' '.$t['Result'];
    }
}

试试吧,只需要做计数器来获得 xml 的深度,在你的情况下是 4 个级别。

【讨论】:

  • 以上代码仅打印 4 条记录,因为那是原始问题。
  • 谢谢,不过我还是希望能找到一个好的XPath表达式——因为我的真实文件比较复杂……
  • 哈哈! :D 请注意,该问题已标记为 XPath。这实际上是 DOM 遍历(最糟糕的情况)
  • @hek2mgl que 问题不是关于 XPath,如果你读到他想用 PHP 选择某个节点的标题,这段代码可以做到。 XPath 有其他答案。
  • 检查标签(它们是为了那个)
猜你喜欢
  • 2014-07-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-30
  • 2019-04-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多