使用 SimpleXMLElement 时添加命名空间

发布于 2024-11-27 23:33:05 字数 1093 浏览 0 评论 0原文

这就是我所追求的

<!-- 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:

This is what I am after

<!-- 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>

This is my current code

<!-- 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());

but it outputs

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

As you can see the ws: is missing from Test

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

┾廆蒐ゝ 2024-12-04 23:33:05

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 会这样做。

SimpleXML has an unusual quirk where the namespace prefixes are filtered from the root element. I'm not sure why it does this.

However, a workaround I've used has been to basically prefix the prefix, so that the parser only removes the first ones, and leaves the second

$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');

This seems to work for me, though I'm interested to understand why SimpleXML does this exactly.

甜宝宝 2024-12-04 23:33:05

问题

这段代码的问题出在第一行:

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

在做任何其他事情之前,让我们将其输出为 XML:

echo $xmlTest->asXML();

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

这是有道理的,我们得到了我们输入的内容。

该手册对于 $ns 的内容相当模糊 参数确实如此,但在这种情况下它没有做任何有用的事情。它的作用是设置读取 XML 的上下文,以便 ->foo 引用 >['bar'] 指的是 ws:bar="..."。它不会改变 XML 本身的结构。

设置根命名空间

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

$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/"make 元素上的 somename2 元素
  • xmlns:ws="ws",然后由 model 元素继承

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

<?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>

太酷了,所以 somename2 元素现在从根元素继承其命名空间定义,并且不会重新声明它。但是 makemodel 有什么问题呢?让我们比较一下:

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

第三个参数应该是命名空间 URI,而不仅仅是前缀。因此,当我们将其指定为 'ws' 时,SimpleXML 假定我们想要将实际的命名空间 URI 声明为 ws,因此添加了一个 xmlns 属性这样做。

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

$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 没有什么特别的在这里,它同样可以是变量、类常量或命名空间常量。代码的运行方式完全相同,只是看起来更容易一些。

The Problem

The problem with this code is in the very first line:

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

Before doing anything else, let's output this as XML:

echo $xmlTest->asXML();

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

That makes sense, we got out what we put in.

The manual is rather vague on what the $ns argument does, but in this case it is not doing anything useful. What it does is set the context for reading the XML, so that ->foo refers to <ws:foo> and ['bar'] refers to ws:bar="...". It doesn't do anything to change the structure of the XML itself.

Setting a Root Namespace

To set a namespace on the root element, we just have to include it in our string defining the root element:

$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/"/>

So far so good...

Setting Namespaces on Children

Next, let's sanity check what the code in the question actually outputs (I've added some whitespace to make it more readable):

<?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>

OK, so this document contains two namespace declarations:

  • xmlns:ws="http://microsoft.com/wsdl/types/" on the somename2 element
  • xmlns:ws="ws" on the make element, which is then inherited by the model elements

What happens if we add our corrected root element?

<?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>

Cool, so the somename2 element now inherits its namespace definition from the root element, and doesn't re-declare it. But what's wrong with the make and models? Let's compare:

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

That third argument is supposed to be the namespace URI, not just the prefix. So when we gave it as 'ws', SimpleXML assumed we wanted to declare the actual namespace URI as ws, so added an xmlns attribute to do so.

What we actually wanted was all the elements to be in the same namespace:

$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>

Great, we've got our desired output!

Tidying Up

But that code looks rather ugly, why do we have to repeat the URI everywhere? Well, as far as SimpleXML is concerned, there's not much choice: the same prefix can mean different things in different parts of the document, so we have to tell it what we want.

What we can do is tidy up our code using a variable or constant for the namespace URI, rather than writing it out in full each time:

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);

There's nothing special about the name XMLNS_WS here, and it could equally be a variable, a class constant, or a namespace constant. The code runs exactly the same, it's just a little easier on the eye.

一笑百媚生 2024-12-04 23:33:05
$xmlTest = new \SimpleXMLElement('<ws:Test></ws:Test>', LIBXML_NOERROR, false, 'ws', true);
$xmlTest->addAttribute('xmlns:xmlns:ws', 'http://url.to.namespace');
$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');
$xmlTest = new \SimpleXMLElement('<ws:Test></ws:Test>', LIBXML_NOERROR, false, 'ws', true);
$xmlTest->addAttribute('xmlns:xmlns:ws', 'http://url.to.namespace');
$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');
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文