【问题标题】:adding a namespace when using SimpleXMLElement使用 SimpleXMLElement 时添加命名空间
【发布时间】:2011-10-19 03:30:00
【问题描述】:

这就是我所追求的

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

【问题讨论】:

    标签: php simplexml


    【解决方案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 为何会这样做。

    【讨论】:

    • 所以在前缀中添加前缀解决了这个问题:)
    • 您是否找到了除此解决方法之外的任何其他解决方案?
    • 更正确的方法可以在这里找到,stackoverflow.com/questions/6808578/…
    • 请注意,这会将“命名空间”添加为属性,而不是命名空间,尽管使用SimpleXMLElement-&gt;asXML() 序列化时的结果是等效的。 “xmlns:”是属性名称local part 的一部分,而不是前缀。请注意,它们出现在 $xmlTest-&gt;attributes() 中,但不在 $xmlTest-&gt;getDocNamespaces() 中。您还可以使用任何前缀,而不仅仅是“xmlns”。
    • 在问题的代码中没有“从根元素中过滤”命名空间,OP 只是没有添加任何名称空间 - 它们的根元素是 &lt;Test /&gt;。当读取该元素时指定命名空间参数'ws'不会神奇地导致该元素在写回时位于命名空间中。
    【解决方案2】:

    问题

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

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

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

    echo $xmlTest->asXML();
    
    <?xml version="1.0"?>
    <Test/>
    

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

    手册对$ns 参数的作用相当模糊,但在这种情况下,它没有做任何有用的事情。它所做的是设置读取 XML 的上下文,以便-&gt;foo 引用&lt;ws:foo&gt;['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/"somename2 元素上
    • xmlns:ws="ws"make 元素上,然后由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 元素现在从根元素继承其命名空间定义,并且不重新声明它。但是makemodels 有什么问题?对比一下:

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

    【讨论】:

    • 其实你不需要对子元素重复相同的命名空间,它们会继承父元素指定的相同命名空间。
    【解决方案3】:
    $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');
    

    【讨论】:

    • 请添加一些解释。
    • 虽然这段代码可以回答这个问题,但最好包含一些上下文,解释它是如何工作的以及何时使用它。从长远来看,纯代码的答案没有用处。
    猜你喜欢
    • 2012-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-07
    • 2010-12-10
    • 2012-10-22
    相关资源
    最近更新 更多