我正在努力实现一些我确信应该简单的事情。这是我的目标:
<s:Envelope
xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:a="http://www.w3.org/2005/08/addressing"
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
我最接近的失败尝试是:
$envelope = $this->doc->createElementNS('http://www.w3.org/2003/05/soap-envelope', 's:Envelope');
$envelope->setAttributeNS('http://www.w3.org/2003/05/soap-envelope', 'xmlns:u', 'http://www.w3.org/2005/08/addressing');
$envelope->setAttributeNS('http://www.w3.org/2003/05/soap-envelope', 'xmlns:u', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd');
提供:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
s:a="http://www.w3.org/2005/08/addressing"
s:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
我已经尝试了几十个参数组合,特别是在setAttributeNS的参数1中,包括&#39; {{3}}&#39;,这些都会给我一个名称空间错误或者只是被忽略。< / p>
难道不难,呃......
答案 0 :(得分:0)
定义“简单”:)。您关注 XML命名空间,具体而言您要创建保留属性(即以“xml”开头的属性,如“xmlns”)并设置它们的值。
这里有些事情发生了冲突。首先, DOMDocument 为您管理命名空间。 XML中的命名空间首先具有URI(命名空间名称)。这是一个通常看起来像URL的标识符,例如“http://www.w3.org/2003/05/soap-envelope
”或“http://www.w3.org/2005/08/addressing
”。如果每个节点始终以该命名空间URI作为前缀,文档的大小将会增大,有多种方法可以将每个命名空间URI映射到较短的表示形式,即名称空间前缀(例如“s
”中的“s:Envelope
” {1}}“)。
让我们拿肥皂信封元素:
<{http://www.w3.org/2003/05/soap-envelope}Envelope />
不是用这种方式写出那个信封元素,而是完成:
<Envelope xmlns="http://www.w3.org/2003/05/soap-envelope" />
这意味着envelope
元素位于http://www.w3.org/2003/05/soap-envelope
命名空间中。
请注意,名为“xmlns
”的属性是保留的,因为其名称以“xml
”开头。它具有特殊含义,它也是命名空间名称“http://www.w3.org/XML/1998/namespace
”的隐式或显式绑定。在示例中,它设置了它所属元素的命名空间,这里是envelope
元素。
由于您可以在同一文档中拥有多个命名空间,并且您不希望为每个节点(例如元素)重复完整URI,因此也可以定义前缀,然后使用前缀。这就是你已经做的,你将前缀“s
”设置为命名空间名称“http://www.w3.org/2003/05/soap-envelope
”:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" />
让我们看一下普通的PHP代码:
$doc = new DOMDocument();
$envelope = $doc->createElementNS('http://www.w3.org/2003/05/soap-envelope', 'Envelope');
$doc->appendChild($envelope);
这会创建名为“Envelope
”的元素,名称空间名称为“http://www.w3.org/2003/05/soap-envelope
”:
<?xml version="1.0"?>
<Envelope xmlns="http://www.w3.org/2003/05/soap-envelope"/>
由于您还希望控制将哪个名称空间前缀用于该名称空间(此处为“s
”),您也可以添加它(如您所知)已经自己了):
$doc = new DOMDocument();
$envelope = $doc->createElementNS('http://www.w3.org/2003/05/soap-envelope', 's:Envelope');
$doc->appendChild($envelope);
导致以下XML:
<?xml version="1.0"?>
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"/>
现在,您要在同一元素上添加的下一个属性(例如“xmlns:a
”)将再次保留(因为它以“xml
”开头)。您尝试将其添加为:
$envelope->setAttributeNS('http://www.w3.org/2003/05/soap-envelope', 'xmlns:a', 'http://www.w3.org/2005/08/addressing');
但它不会创建您想要实现的结果:
<?xml version="1.0"?>
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" s:a="http://www.w3.org/2005/08/addressing"/>
结果XML显示并且仔细查看代码现在可能更明显,您不希望添加名称空间名称为“http://www.w3.org/2003/05/soap-envelope
”的属性({{1的第一个参数}})。相反,您希望添加属于(保留)命名空间名称“setAttributeNS
”的属性,因为该属性名称(“http://www.w3.org/XML/1998/namespace
”)以“xmlns:a
”开头,因此保留。
幸运的是,正如我之前写的那样,这些属性的名称空间是隐含的。您可以像“普通”属性一样添加它们,即没有任何命名空间:
xml
然后会产生您正在寻找的XML:
$envelope->setAttribute('xmlns:a', 'http://www.w3.org/2005/08/addressing');
之前已经回答过,但我现在找不到现有的问答材料。
那就简单了。您错误地使用错误的命名空间名称添加属性,很可能是因为您不知道如何直接添加属性。请注意,以“<?xml version="1.0"?>
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing"/>
”开头的所有属性(不区分大小写)都是保留,并且它们具有隐含含义。
例如,添加具有第二个命名空间名称的子元素将正确添加到文档中:
xml
但结果很冗长:
$test = $doc->createElementNS('http://www.w3.org/2005/08/addressing', 'a:test');
$doc->documentElement->appendChild($test);
添加的“<?xml version="1.0"?>
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
<a:test xmlns:a="http://www.w3.org/2005/08/addressing"/>
</s:Envelope>
元素具有正确的名称空间声明(”a:test"
“)。严格来说,这不是必要的(但它没有伤害,这是正确的),这是因为"Namespaces are not defined on or for a XML document, but on element nodes"。如果已经在文档元素上指定了所有名称空间前缀,则可以重新加载文档,然后相同的代码将不会添加额外的定义:
xmlns:a="http://www.w3.org/2005/08/addressing"
该文件的输出显示:
$doc = new DOMDocument();
$envelope = $doc->createElementNS('http://www.w3.org/2003/05/soap-envelope', 's:Envelope');
$doc->appendChild($envelope);
$envelope->setAttribute('xmlns:a', 'http://www.w3.org/2005/08/addressing');
$doc->loadXML($doc->saveXML()); # reload the document
$test = $doc->createElementNS('http://www.w3.org/2005/08/addressing', 'test');
$doc->documentElement->appendChild($test);
这是因为在重新加载文档时, DOMDocument 对象知道哪些名称空间已经加前缀,并且它可以重用该名称空间名称的前缀。从技术上讲,这不是必需的(文档是相同的),如果您担心创建文档的相同文本XML序列化,这可能对您有意义。但它不应该是必要的。以及前缀必须完全匹配。
所以你认为简单的实际上非常复杂(想象一下已经为该答案读取的文本长度)。而是在您需要的地方添加命名空间元素和属性,并让扩展程序负责它以正确注册命名空间。这就是API的用途。不要过分关注文本表示(即使我在这里向您展示了如何创建相同的文本结果)。关键是要了解命名空间以及如何使用API。只是寻找相同的文本表示更多(通常是太多)工作 - 但可能但不简单。