PHP/SimpleXML/XPath通过同一元素中的另一个属性获取属性值


PHP/SimpleXML/XPath get attribute value by another attribute in same element

我有这个XML(来自pptx文件):

<Relationships>
    <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="../media/image2.png"/>
    <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="../media/image1.wmf"/>
    <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" Target="../slideLayouts/slideLayout1.xml"/>
</Relationships>

我想从Relationship元素中提取Target属性,并且我知道Id值。

如果我遍历节点(像这个问题一样),我可以用SimpleXML来完成它

$resxml = simplexml_load_file('zip://my.pptx#ppt/slides/_rels/slide1.xml.rels');
echo $resxml->Relationship[0]->attributes()->Target;

但我想使用xpath,使用这种想法来获得它。当我搜索类似"rId3"的东西时,无论我在xpath中做什么,都会返回一个空对象。我原以为是下面的xpath语句,但它返回了一个空对象。我尝试了大约50种组合,在搜索时发现了很多相似但不完全相同的问题:

$image = $resxml->xpath("/Relationships/Relationship[@Id='rId3']/@Target"); 
print_r($image);

我想我最终会遍历所有节点,但这似乎效率很低。我的服务器似乎在Dom中有XPath可用,并且启用了SimpleXML。

谢谢。你出色的回答是我找到解决方案的关键。在阅读了您的文章后,我在Stack exchange的其他地方发现SimpleXML删除了第一个节点上的名称空间属性。我考虑了名称空间作为问题,但在查看树时只查看了simpleXML输出。你在看真正的来源时把我说对了。

我仅使用简单XML的解决方案如下:

$resxml->registerXPathNamespace('r', 'http://schemas.openxmlformats.org/package/2006/relationships');
$image = $resxml->xpath("/r:Relationships/r:Relationship[@Id='rId3']/@Target"); 
print_r($image);

我认为问题可能是命名空间。PPTX关系文件使用命名空间"http://schemas.microsoft.com/package/2005/06/relationships"。但是SimpleXmls-xpath也有它自己的魔力。如果文件包含命名空间(检查源),你必须为它注册一个自己的前缀。

$xml = <<<'XML'
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<Relationships
 xmlns="http://schemas.microsoft.com/package/2005/06/relationships">
 <Relationship Id="rId1"
 Type="http://schemas.microsoft.com/office/2006/relationships/image"
 Target="http://en.wikipedia.org/images/wiki-en.png"
 TargetMode="External" />
 <Relationship Id="rId2"
 Type="http://schemas.microsoft.com/office/2006/relationships/hyperlink"
 Target="http://www.wikipedia.org"
 TargetMode="External" />
</Relationships> 
XML;
$dom = new DOMDocument();
$dom->loadXml($xml);
$xpath = new DOMXpath($dom);
$xpath->registerNamespace('r', 'http://schemas.microsoft.com/package/2005/06/relationships');
var_dump(
  $xpath->evaluate("string(/r:Relationships/r:Relationship[@Id='rId2']/@Target)", NULL, FALSE)
);

输出:

string(24) "http://www.wikipedia.org"

Xpath不知道类似默认名称空间的东西。如果没有前缀,则可以查找没有任何命名空间的元素。如果没有显式前缀,则属性没有命名空间。

为了消除混淆,PHP函数(SimpleXMLElement::xpath()、DOMXpath::query()和DOMXpath::evaluate())会自动注册所用上下文的命名空间定义。第三个参数允许禁用这种行为。

与其他两个函数不同,DOMXpath::evaluate()可以直接返回标量。