SOAP Envelope中的多个名称空间属性

时间:2017-07-01 22:12:53

标签: php soap namespaces

我正在努力实现一些我确信应该简单的事情。这是我的目标:

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

难道不难,呃......

1 个答案:

答案 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​​。只是寻找相同的文本表示更多(通常是太多)工作 - 但可能但不简单。