【问题标题】:How can I find out the namespace of an element in PHP DOM?如何在 PHP DOM 中找出元素的命名空间?
【发布时间】:2010-08-25 13:58:26
【问题描述】:

这听起来像是一个很容易回答的问题,但我无法让它发挥作用。我正在运行 PHP 5.2.6。

我有一个 DOM 元素(根元素),当我转到 $element->saveXML() 时,它会输出一个 xmlns 属性:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
...

但是,我在 PHP 中找不到任何以编程方式查看该命名空间的方法。我希望能够检查它是否存在以及它的设置。

检查$document-&gt;documentElement-&gt;namespaceURI 将是显而易见的答案,但它是空的(我实际上从来没有能够让它非空)。是什么在输出中生成了该 xmlns 值,我该如何读取它?

到目前为止,我能够做到这一点的唯一实用方法是完全破解 - 使用 saveXML() 将其保存为 XML 到字符串,然后使用正则表达式读取该字符串。

编辑:

这可能是使用 loadHTML() 而不是 loadXML() 加载 XML,然后使用 saveXML() 打印出来的特性。当您这样做时,似乎出于某种原因 saveXML 添加了一个 xmlns 属性,即使使用 DOM 方法无法检测到此 xmlns 值是文档的一部分。我猜这意味着如果我有办法检测传入的文档是否已使用 loadHTML() 加载,那么我可以用不同的方式解决这个问题。

【问题讨论】:

    标签: php dom domdocument xml-namespaces


    【解决方案1】:

    Like edorian already showed,当使用loadXML 加载标记时,获取命名空间工作正常。但你是对的,这不适用于加载了 loadHTML 的标记:

    $html = <<< XML
    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <html xmlns="http://www.w3.org/1999/xhtml" xmlns:m="foo" lang="en">
        <body xmlns="foo">Bar</body>
    </html>
    XML;
    
    $dom = new DOMDocument;
    $dom->loadHTML($html);
    
    var_dump($dom->documentElement->getAttribute("xmlns"));
    var_dump($dom->documentElement->lookupNamespaceURI(NULL));
    var_dump($dom->documentElement->namespaceURI);
    

    将产生空结果。但是你可以使用 XPath

    $xp = new DOMXPath($dom);
    echo $xp->evaluate('string(@xmlns)');
    // http://www.w3.org/1999/xhtml;
    

    对于身体

    echo $xp->evaluate('string(body/@xmlns)'); // foo
    

    或使用上下文节点

    $body = $dom->documentElement->childNodes->item(0);
    echo $xp->evaluate('string(@xmlns)', $body);
    // foo
    

    我未受过教育的假设是,在内部,HTML 文档与 真实 文档不同。在内部 libxml uses a different module to parse HTML 和 DOMDocument 本身将是不同的 nodeType,您可以通过这样做来简单地验证

    var_dump($dom->nodeType); // 13 with loadHTML, 9 with loadXml
    

    其中 13 是 XML_HTML_DOCUMENT_NODE

    【讨论】:

    • 非常漂亮和详细,不知道 nodeTypes 取决于解析方法,但它是有道理的
    • 感谢有关节点类型的提示和使用 xpath 的能力 - 解决了我的很多问题!
    【解决方案2】:

    使用 PHP 5.2.6,我找到了两种方法:

    <?php
    $xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?'.
           '><html xmlns="http://www.w3.org/1999/xhtml" lang="en"></html>';
    $x = DomDocument::loadXml($xml);
    var_dump($x->documentElement->getAttribute("xmlns"));
    var_dump($x->documentElement->lookupNamespaceURI(NULL));
    

    打印

    string(28) "http://www.w3.org/1999/xhtml"
    string(28) "http://www.w3.org/1999/xhtml"
    

    希望这就是你所要求的:)

    【讨论】:

    • 感谢您的回答 - 它不能解决我的问题,但提示我它似乎是从 loadHTML() 而不是 loadXML() 加载的文档所特有的东西,因为确实,您的示例有效使用 loadXML()。看起来 loadHTML 使用“不可见的命名空间”创建文档,该命名空间无法使用 DOM 方法读取,但会在您 saveXML() 时出现。
    • 我不确定我是否能 100% 关注你,但使用 loadHtml 加载内容并通过 saveXml 重新保存不会为我添加 xmlns。它只是从 html 添加/保留一个文档类型。也许如果你能在你想要的输出旁边提供一个小重现脚本,我可以更深入地挖掘
    • 有趣 - 它有时会,有时不会。如果您的输入 HTML 文档有一个 XHTML DOCTYPE,它就有。它将为此输入执行此操作:ttp://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" rel="nofollow" target="_blank">w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    • 我不知道你会如何在 DOM 中检测到这一点。
    【解决方案3】:

    嗯,你可以用这样的函数来做到这一点:

    function getNamespaces(DomNode $node, $recurse = false) {
        $namespaces = array();
        if ($node->namespaceURI) {
            $namespaces[] = $node->namespaceURI;
        }
        if ($node instanceof DomElement && $node->hasAttribute('xmlns')) {
            $namespaces[] = $xmlns = $node->getAttribute('xmlns');
            foreach ($node->attributes as $attr) {
                if ($attr->namespaceURI == $xmlns) {
                    $namespaces[] = $attr->value;
                    }
            }
        }
        if ($recurse && $node instanceof DomElement) {
            foreach ($node->childNodes as $child) {
                $namespaces = array_merge($namespaces, getNamespaces($child, vtrue));
            }
        }
        return array_unique($namespaces);
    }
    

    所以,你给它一个 DomEelement,然后它会找到所有相关的命名空间:

    $xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
        <html xmlns="http://www.w3.org/1999/xhtml" 
             lang="en" 
             xmlns:foo="http://example.com/bar">
               <body>
                    <h1>foo</h1>
                    <foo:h2>bar</foo:h2>
               </body>
     </html>';
    var_dump(getNamespaces($dom->documentElement, true));
    

    打印出来:

    array(2) {
      [0]=>
      string(28) "http://www.w3.org/1999/xhtml"
      [3]=>
      string(22) "http://example.com/bar"
    }
    

    请注意,DomDocument 会自动删除所有未使用的命名空间...

    至于为什么$dom-&gt;documentElement-&gt;namespaceURI总是null,是因为文档元素没有命名空间。 xmlns 属性为文档提供了默认命名空间,但它没有赋予 html 标签命名空间(用于 DOM 交互)。你可以试试$dom-&gt;documentElement-&gt;removeAttribute('xmlns'),但我不能 100% 确定它是否会起作用...

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-08-27
      • 1970-01-01
      • 1970-01-01
      • 2014-04-24
      • 2018-04-04
      • 2010-10-06
      • 1970-01-01
      相关资源
      最近更新 更多