SimpleXMLElement-> xpath()显示不在对象中的数据

时间:2013-10-26 01:17:23

标签: php xpath simplexml

我查询YouTrack的网络服务以获取问题列表。响应是XML,如下所示:

<issueCompacts>
    <issue id="XX-1">
        <field xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CustomField" name="Type">
            <value>Bug</value>
        </field>
        <field xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CustomField" name="Bill-to">
            <value>NBS</value>
        </field>
    </issue>
    <issue id="XX-2">
        <field xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CustomField" name="Type">
            <value>New Feature</value>
        </field>
        <field xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CustomField" name="Bill-to">
            <value>NBS</value>
        </field>
    </issue>
    [...]
</issueCompacts>

我采用这个实际包含五个问题的XML,并从中创建一个名为$ issuesObj的SimpleXMLElement对象(注意复数&#39;问题&#39;)。然后我重复讨论这些问题:

foreach ($issuesObj as $issueObj) {
    [...]
} //foreach

在这个循环中(注意单数&#39;问题&#39; as变量),如果我var_dump()$ issueObj,我得到这个:

object(SimpleXMLElement)#4 (2) {
  ["@attributes"]=>
  array(1) {
    ["id"]=>
    string(4) "XX-1"
  }
  ["field"]=>
  array(2) {
    [0]=>
    object(SimpleXMLElement)#16 (2) {
      ["@attributes"]=>
      array(1) {
        ["name"]=>
        string(4) "Type"
      }
      ["value"]=>
      string(3) "Bug"
    }
    [1]=>
    object(SimpleXMLElement)#17 (2) {
      ["@attributes"]=>
      array(1) {
        ["name"]=>
        string(7) "Bill-to"
      }
      ["value"]=>
      string(3) "NBS"
    }
  }
}

这里是$ issueObj-&gt; asXml()的转储:

<?xml version="1.0"?>
<issue id="XX-1">
    <field xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CustomField" name="Type">
        <value>Bug</value>
    </field>
    <field xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CustomField" name="Bill-to">
        <value>NBS</value>
    </field>
</issue>

到目前为止,这完全符合预期。但是,这就是它变得奇怪的地方。我想在名称为&#39; Type&#39;的问题中挑选字段,因此我使用xpath()方法:

$typeField = $issueObj->xpath('//field[@name="Type"]')

现在,如果我转储$ typeField,我得到这个:

array(5) {
  [0]=>
  object(SimpleXMLElement)#10 (2) {
    ["@attributes"]=>
    array(1) {
      ["name"]=>
      string(4) "Type"
    }
    ["value"]=>
    string(3) "Bug"
  }
  [1]=>
  object(SimpleXMLElement)#11 (2) {
    ["@attributes"]=>
    array(1) {
      ["name"]=>
      string(4) "Type"
    }
    ["value"]=>
    string(11) "New Feature"
  }
  [2]=>
  object(SimpleXMLElement)#13 (2) {
    ["@attributes"]=>
    array(1) {
      ["name"]=>
      string(4) "Type"
    }
    ["value"]=>
    string(11) "New Feature"
  }
  [3]=>
  object(SimpleXMLElement)#14 (2) {
    ["@attributes"]=>
    array(1) {
      ["name"]=>
      string(4) "Type"
    }
    ["value"]=>
    string(3) "Bug"
  }
  [4]=>
  object(SimpleXMLElement)#15 (2) {
    ["@attributes"]=>
    array(1) {
      ["name"]=>
      string(4) "Type"
    }
    ["value"]=>
    string(11) "New Feature"
  }
}

请注意,有五个元素而不是预期的两个元素。似乎正在发生的是xpath()方法作用于原始的$ issuesObj,而不是作为子集的$ issueObj。但是,它变得更加奇怪。如果我将$ issueObj转换为XML,然后使用该XML直接返回到对象,它就可以工作。因此:

$issueObj = new \SimpleXMLElement($issueObj->asXml());
$typeField = $issueObj->xpath('//field[@name="Type"]');
var_dump($typeField); exit;

得出这个:

array(1) {
  [0]=>
  object(SimpleXMLElement)#17 (2) {
    ["@attributes"]=>
    array(1) {
      ["name"]=>
      string(4) "Type"
    }
    ["value"]=>
    string(3) "Bug"
  }
}

这是正确的。并且调用$ typeField [0] - &gt; asXml()现在产生这个:

<?xml version="1.0"?>
<field xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CustomField" name="Type">
    <value>Bug</value>
</field>

再次与预期完全一样。

有关SimpleXMLElement为何如此表现的任何线索?

2 个答案:

答案 0 :(得分:3)

问题在于XPath如何处理 context This page on MSDN虽然显然是在谈论完全不同的实现,但却有一个简洁的解释:

  

使用双正斜杠(//)的表达式表示可以包含零级或多级层次结构的搜索。当此运算符出现在模式的开头时,上下文相对于文档的根目录。

所以foo//bar从“当前”节点开始,找到名为foo的子项,然后递归搜索其后代bar;但//bar跳转到文档的顶部,并递归搜索任何名为bar的节点。

要明确引用当前上下文,您可以使用.,因此.//field[@name="Type"]应该可以正常工作。

因为在您的情况下,field元素是当前节点的直接子元素,所以无论如何都不需要递归//,因此./field[@name="Type"]和{{1}也应该工作。


Aditional note :标题中的“不在对象中”这句话表明您认为SimpleXML已将XML文件转换为一堆PHP对象。实际情况并非如此;相反,将SimpleXML视为用于在内存中处理已解析的XML文档的API,并将每个SimpleXMLElement视为指向该已解析文档的指针。 API被设计为在PHP中感觉“自然”,但包含比大多数PHP对象更多的“魔力”。

答案 1 :(得分:1)

查看xpath语法:http://www.w3schools.com/xpath/xpath_syntax.asp 例如:// title [@lang]选择具有名为lang

的属性的所有标题元素

改变这个:

$typeField = $issueObj->xpath('//field[@name="Type"]');

到此:

$typeField = $issueObj->xpath('field[@name="Type"]');
相关问题