使用 SimpleXMLElement 时添加命名空间问题设置根命名空间在子项上设置命名空间整理

2022-08-30 22:49:12

这就是我所追求的

<!-- language: lang-xml -->
<ws:Test>
    <ws:somename2>somevalue2</ws:somename2>
    <ws:make>
        <ws:model>foo</ws:model>
        <ws:model>bar</ws:model>
    </ws:make>
</ws:Test>

这是我当前的代码

<!-- language: lang-php -->
$xmlTest = new SimpleXMLElement('<Test/>', 0, false, 'ws', true);
$xmlTest->addChild("ws:somename2", "somevalue2", 'http://microsoft.com/wsdl/types/');
$make = $xmlTest->addChild('ws:make', null, 'ws');
#$make->addAttribute('name','Ford');
$make->addChild('ws:model', 'foo', 'ws');
$make->addChild('ws:model', 'bar', 'ws');
header ("Content-Type:text/xml");
print_r($xmlTest->asXML());

但它输出

<!-- language: lang-xml -->
<Test>
    <ws:somename2>somevalue2</ws:somename2>
    <ws:make>
        <ws:model>foo</ws:model>
        <ws:model>bar</ws:model>
    </ws:make>
</Test>

如您所见,测试中缺少 ws:


答案 1

SimpleXML 有一个不寻常的怪癖,其中命名空间前缀是从根元素中筛选出来的。我不确定它为什么会这样做。

但是,我使用的解决方法是基本上为前缀添加前缀,以便解析器仅删除第一个前缀,并保留第二个前缀。

$xmlTest = new SimpleXMLElement('<xmlns:ws:Test></xmlns:ws:Test>', LIBXML_NOERROR, false, 'ws', true);
$xmlTest->addAttribute('xmlns:xmlns:ws', 'http://url.to.namespace');
$xmlTest->addAttribute('xmlns:xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');

这似乎对我有用,尽管我有兴趣了解为什么SimpleXML会这样做。


答案 2

问题

此代码的问题在第一行:

$xmlTest = new SimpleXMLElement('<Test/>', 0, false, 'ws', true);

在执行其他任何操作之前,让我们将其输出为 XML:

echo $xmlTest->asXML();

<?xml version="1.0"?>
<Test/>

这是有道理的,我们拿出了我们投入的东西。

该手册对论点的作用相当模糊,但在这种情况下,它没有做任何有用的事情。它的作用是设置用于读取 XML 的上下文,以便引用和引用 。它不会对更改 XML 本身的结构执行任何操作。$ns->foo<ws:foo>['bar']ws:bar="..."

设置根命名空间

要在根元素上设置命名空间,我们只需要将其包含在定义根元素的字符串中:

$xmlTest = new SimpleXMLElement('<ws:Test xmlns:ws="http://microsoft.com/wsdl/types/" />');
echo $xmlTest->asXML();

<?xml version="1.0"?>
<ws:Test xmlns:ws="http://microsoft.com/wsdl/types/"/>

目前为止,一切都好。。。

在子项上设置命名空间

接下来,让我们理智地检查问题中的代码实际输出的内容(我添加了一些空格以使其更具可读性):

<?xml version="1.0"?> 
<Test>
   <ws:somename2 xmlns:ws="http://microsoft.com/wsdl/types/">somevalue2</ws:somename2>
   <ws:make xmlns:ws="ws">
        <ws:model>foo</ws:model>
        <ws:model>bar</ws:model>
   </ws:make>
</Test>

好的,所以本文档包含两个命名空间声明:

  • xmlns:ws="http://microsoft.com/wsdl/types/"在元素上somename2
  • xmlns:ws="ws",然后由元素继承makemodel

如果我们添加更正后的根元素会发生什么情况?

<?xml version="1.0"?> 
<ws:Test xmlns:ws="http://microsoft.com/wsdl/types/">
    <ws:somename2>somevalue2</ws:somename2>
    <ws:make xmlns:ws="ws">
        <ws:model>foo</ws:model>
        <ws:model>bar</ws:model>
   </ws:make>
</ws:Test>

很酷,因此该元素现在从根元素继承其命名空间定义,并且不会重新声明它。但是 and s 有什么问题呢?我们来比较一下:somename2makemodel

$xmlTest->addChild("ws:somename2", "somevalue2", 'http://microsoft.com/wsdl/types/');
$make = $xmlTest->addChild('ws:make', null, 'ws');

第三个参数应该是命名空间 URI,而不仅仅是前缀。因此,当我们将其命名为,SimpleXML 假设我们想要将实际的命名空间 URI 声明为 ,因此添加了一个属性来执行此操作。'ws'wsxmlns

我们真正想要的是所有元素都在同一命名空间中:

$xmlTest = new SimpleXMLElement('<ws:Test xmlns:ws="http://microsoft.com/wsdl/types/" />');
$xmlTest->addChild("ws:somename2", "somevalue2", 'http://microsoft.com/wsdl/types/');
$make = $xmlTest->addChild('ws:make', null, 'http://microsoft.com/wsdl/types/');
#$make->addAttribute('name','Ford', 'http://microsoft.com/wsdl/types/');
$make->addChild('ws:model', 'foo', 'http://microsoft.com/wsdl/types/');
$make->addChild('ws:model', 'bar', 'http://microsoft.com/wsdl/types/');
echo $xmlTest->asXML();

<?xml version="1.0"?> 
<ws:Test xmlns:ws="http://microsoft.com/wsdl/types/">
    <ws:somename2>somevalue2</ws:somename2>
    <ws:make>
        <ws:model>foo</ws:model>
        <ws:model>bar</ws:model>
   </ws:make>
</ws:Test>

太好了,我们有了我们想要的输出!

整理

但是该代码看起来相当丑陋,为什么我们必须在任何地方重复URI?好吧,就SimpleXML而言,没有太多选择:相同的前缀在文档的不同部分中可能意味着不同的东西,因此我们必须告诉它我们想要什么。

我们可以做的是使用命名空间URI的变量或常量来整理我们的代码,而不是每次都将其完整地写出来:

define('XMLNS_WS', 'http://microsoft.com/wsdl/types/');

$xmlTest = new SimpleXMLElement('<ws:Test xmlns:ws="' . XMLNS_WS . '" />');
$xmlTest->addChild("ws:somename2", "somevalue2", XMLNS_WS);
$make = $xmlTest->addChild('ws:make', null, XMLNS_WS);
#$make->addAttribute('name','Ford', XMLNS_WS);
$make->addChild('ws:model', 'foo', XMLNS_WS);
$make->addChild('ws:model', 'bar', XMLNS_WS);

这里的名称没有什么特别之处,它同样可以是变量、类常量或命名空间常量。代码运行完全相同,只是看起来更容易一些。XMLNS_WS


推荐