定义“简单”:)。您关心 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:Envelope”中的“s” ")。
让我们来看看soap信封元素吧:
<{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”(setAttributeNS 的第一个参数)的属性。相反,您想添加一个位于(保留)命名空间名称“http://www.w3.org/XML/1998/namespace”中的属性,因为该属性名称(“xmlns:a”)以“xml”开头,因此是保留的。
幸运的是,正如我之前所写,这些属性具有隐含的命名空间。您可以像“普通”属性一样添加它们,即没有任何命名空间:
$envelope->setAttribute('xmlns:a', 'http://www.w3.org/2005/08/addressing');
然后生成您正在寻找的 XML:
<?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"”元素具有正确的命名空间声明(“xmlns:a="http://www.w3.org/2005/08/addressing"”)。严格来说,这不是必需的(但它没有伤害,它是正确的),那是因为"Namespaces are not defined on or for a XML document, but on element nodes"。当您已经在文档元素上指定了所有命名空间前缀时,您可以重新加载文档,然后相同的代码将不会添加该额外定义:
$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);
文档的输出显示如下:
<?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/>
</s:Envelope>
这是因为在重新加载文档时,DOMDocument 对象知道哪些命名空间已经添加了前缀,并且它可以重新使用该命名空间名称的前缀。从技术上讲,这不是必需的(文档是相同的),如果您关心创建文档的相同文本 XML 序列化,这对您来说可能会很有趣。但这不应该是必要的。以及前缀不能完全匹配。
因此,您认为简单的内容实际上非常复杂(想象一下要阅读该答案的文本长度)。相反,在您需要的地方添加命名空间元素和属性,并让扩展处理它以正确注册命名空间。这就是 API 的用途。不要过分关注文本表示(即使我在这里向您展示了如何创建相同的文本结果)。关键是了解命名空间以及如何使用 API。仅仅寻找相同的文本表示是更多(通常是太多)工作 - 但可能但并不简单。