如何在命名空间环境中访问 SimpleXML 节点?

Posted

技术标签:

【中文标题】如何在命名空间环境中访问 SimpleXML 节点?【英文标题】:How do you access SimpleXML nodes in a namespace environment? 【发布时间】:2021-10-09 20:44:12 【问题描述】:

如何在命名空间环境中访问 php SimpleXML 节点?

我想务实地在<request> 下添加<param>value</param>。不仅仅是在一个字符串中编辑它。

$xml = \SimpleXMLElement(
    '<?xml version="1.0" encoding="utf-8"?>' . PHP_EOL
  . '<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">' . PHP_EOL
  . '  <soap:Body>' . PHP_EOL
  . '    <Function xmlns="https://webservices.sveaekonomi.se/webpay">' . PHP_EOL
  . '      <request />' . PHP_EOL
  . '    </Function>' . PHP_EOL
  . '  </soap:Body>' . PHP_EOL
  . '</soap:Envelope>'
);

我尝试了以下方法:

#1
$xml->Envelope->Body->Function->request->addChild('param', 'value');

#2
$xml->children('https://webservices.sveaekonomi.se/webpay')->request->addChild('param', 'value');

#3
$xml->registerXPathNamespace('swp', 'https://webservices.sveaekonomi.se/webpay');
$xml->xpath('/swp:request')->addChild('param', 'value');

【问题讨论】:

【参考方案1】:

你在这里犯了一些常见的错误,所以我将依次进行。

让我们从您的第一次尝试开始:

$xml->Envelope->Body->...

SimpleXML 没有单独的文档对象,只有根元素 - 在本例中为 Envelope。所以你不需要说-&gt;Envelope,你已经在那里了。如果不涉及命名空间,您将编写:

$xml->Body->...

但是,对象不会自动“选择”该元素的命名空间,因此您必须立即调用 -&gt;children(),或者将要预选的命名空间传递给构造函数:

$xml->children('http://schemas.xmlsoap.org/soap/envelope/')->Body->...
// Or
$xml = new SimpleXMLElement($xmlString, 0, false, 'http://schemas.xmlsoap.org/soap/envelope/');
$xml->Body->...

考虑到这一点,我们可以:

$xml->children('http://schemas.xmlsoap.org/soap/envelope/')->Body->Function->...

这会失败,因为 FunctionBody 不在同一个命名空间中。我喜欢的想法是the -&gt;children() method是一个“切换命名空间”的方法,所以我们切换到正确的命名空间并继续:

$xml->children('http://schemas.xmlsoap.org/soap/envelope/')->Body
    ->children('https://webservices.sveaekonomi.se/webpay')->Function->request
    ->addChild('param', 'value');

这行得通!


你的第二次尝试犯了一个不同的错误:

$xml->children('https://webservices.sveaekonomi.se/webpay')->request->...

“children”方法不会让您深入到文档中 - 顾名思义,它为您提供元素的直接子元素,而不是孙子、曾孙-儿童等等。 $xml 变量指向“信封”节点,它没有名为“请求”的子节点。

没有“任何后代”的内置方法,除非使用 XPath...


虽然看起来完全不同,但您的第三次尝试实际上因同样的原因而失败:

$xml->registerXPathNamespace('swp', 'https://webservices.sveaekonomi.se/webpay');
$xml->xpath('/swp:request')->...

XPath 中的/ 运算符同样表示“子级”; “任何后代”的运算符,即//,因此您将几乎得到正确的结果:

$xml->registerXPathNamespace('swp', 'https://webservices.sveaekonomi.se/webpay');
$xml->xpath('//swp:request')->...

这会因为一个更微妙的原因而失败:SimpleXML 的the xpath() method 总是返回一个数组,而不是一个对象,所以你必须要求它的第一个元素 ([0])数组。

所以工作的 XPath 代码是这样的:

$xml->registerXPathNamespace('swp', 'https://webservices.sveaekonomi.se/webpay');
$xml->xpath('//swp:request')[0]->addChild('param', 'value');

【讨论】:

哇,这个答案确实值得赞赏!非常感谢!投票并标记为问题的公认答案! :)【参考方案2】:

由于您正在处理 xml,因此处理它的一种方法是使用 xpath 和 local-name():

$xml = '<?xml version="1.0" encoding="utf-8"?> 
  <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> 
    <soap:Body> 
      <Function xmlns="https://webservices.sveaekonomi.se/webpay"> 
        <request/>
      </Function> 
    </soap:Body> 
  </soap:Envelope>
';
$doc = new SimpleXMLElement($xml);
$destination = ($doc->xpath('//*[local-name()="request"]'));
$destination[0]->addChild('param', 'value');
echo $doc->asXML();

输出:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> 
    <soap:Body> 
      <Function xmlns="https://webservices.sveaekonomi.se/webpay"> 
        <request><param>value</param></request>
      </Function> 
    </soap:Body> 
  </soap:Envelope>

【讨论】:

有趣,我在这里学到了一些新东西:)

以上是关于如何在命名空间环境中访问 SimpleXML 节点?的主要内容,如果未能解决你的问题,请参考以下文章

使用 SimpleXML 使用命名空间解析 XML [重复]

PHP SimpleXML->addChild - 不需要的空命名空间属性

使用 php SimpleXML 解析 XML 命名空间

使用 php SimpleXML 解析 XML 命名空间

使用 xpath 访问具有命名空间的子节点

访问 SimpleXML 节点的 XML 属性