【问题标题】:How to detect and remove unnecessary xmlns:<something> attributes in PHP DOM?如何检测和删除 PHP DOM 中不必要的 xmlns:<something> 属性?
【发布时间】:2011-04-18 03:45:10
【问题描述】:

假设我有这样的源文档:

<element>
  <subelement xmlns:someprefix="mynamespace"/>
</element>

xmlns:someprefix 显然在这里不需要,也不会做任何事情,因为该前缀没有用于该元素(或者在我的情况下,文档中的任何位置)。

在 PHP 中,当我使用 DOMDocument->loadXML() 将其加载到 DOM 树中后,我希望能够检测到这样的命名空间声明是否存在,并将其删除。

我知道我可以用hasAttribute() 阅读它,甚至可以用removeAttributeNS() 删除它(奇怪),但前提是我知道它的前缀。它根本没有出现在DOMNode-&gt;attributes 中,因为我试图找到的东西不被视为属性。除了将其序列化回 XML 字符串并运行正则表达式之类的东西之外,我看不到任何方法可以在不知道前缀的情况下检测到它的存在。

我该怎么做?有什么方法可以查询元素中声明了哪些命名空间(即 xmlns:something)?

【问题讨论】:

  • 我想回答这个问题的一种方法是“别担心”。看起来这些属性根本不影响 DTD 验证(文档仍然有效),它们只是浪费不必要的字节并使文档看起来很丑。所以我现在完全可以忽略这个问题......不过,如果有解决方案就好了。
  • 这确实是唯一的答案。忽略它。在未来的某个时候,您可能会添加一个子 &lt;someprefix:someelement/&gt;,此时,您会想要回命名空间。

标签: php xml dom namespaces xml-namespaces


【解决方案1】:

如何检测:

<?php
$d = new DOMDocument();
$d->loadXML('
<element>
  <subelement xmlns:someprefix="http://mynamespace/asd">
  </subelement>
</element>');
$sxe = simplexml_import_dom($d);
$namespaces = $sxe->getDocNamespaces(true);
$x = new DOMXpath($d);
foreach($namespaces as $prefix => $url){
        $count = $x->evaluate("count(//*[namespace-uri()='".$url."' or @*[namespace-uri()='".$url."']])");
        echo $prefix.' ( '.$url.' ): used '.$count.' times'.PHP_EOL;
}

如何删除:pfff,关于我所知道的唯一选择是使用 xml_parse_into_struct()(因为这不是 libxml2 依赖的 afaik),并使用 XML Writer 函数循环遍历结果数组,跳过命名空间声明,这些声明是未使用。不是一个有趣的消遣,所以我将把实现留给你。根据this question,另一个选项可能是XSL,但我怀疑它是否有用。我的最大努力似乎成功了,但是将“顶级”/rootnode 命名空间移动到子级,导致更加混乱。

编辑:这似乎有效:

给定 XML(添加了一些混乱的命名空间):

<element xmlns:yetanotherprefix="http://mynamespace/yet">
  <subelement
        xmlns:someprefix="http://mynamespace/foo"
        xmlns:otherprefix="http://mynamespace/bar"
        foo="bar"
        yetanotherprefix:bax="foz">
        <otherprefix:bar>
                <yetanotherprefix:element/>
                <otherprefix:element/>
        </otherprefix:bar>
        <otherprefix:bar>
                <yetanotherprefix:element/>
                <otherprefix:element/>
        </otherprefix:bar>
        <yetanotherprefix:baz/>
  </subelement>

使用基于先前 $used 数组的 xsl(命名空间和 not() 子句,因此您仍然需要那个 afaik。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
    xmlns:yetanotherprefix="http://mynamespace/yet"
    xmlns:otherprefix="http://mynamespace/bar"> 
    <xsl:template match="/">
        <xsl:apply-templates select="/*"/>
    </xsl:template>
    <xsl:template match="*">
        <xsl:element name="{name(.)}">
                <xsl:apply-templates select="./@*"/>
                <xsl:copy-of select="namespace::*[not(name()='someprefix')]"/>
                <xsl:apply-templates select="./node()"/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="@*">
        <xsl:copy/>
    </xsl:template>
</xsl:stylesheet>

结果:

<?xml version="1.0"?>
<element xmlns:yetanotherprefix="http://mynamespace/yet">
  <subelement xmlns:otherprefix="http://mynamespace/bar" foo="bar" yetanotherprefix:bax="foz">
        <otherprefix:bar>
                <yetanotherprefix:element/>
                <otherprefix:element/>
        </otherprefix:bar>
        <otherprefix:bar>
                <yetanotherprefix:element/>
                <otherprefix:element/>
        </otherprefix:bar>
        <yetanotherprefix:baz/>
  </subelement>
</element>

【讨论】:

  • 您可以使用 removeAttributeNS() 删除这些命名空间及其相关前缀 - 请参阅此页面上“primaryspace”的评论:php.net/manual/en/domelement.removeattributens.php - 这确实很奇怪,但它确实有效!我唯一坚持的部分是检测首先存在的前缀和命名空间 URL - 但您的第一个示例似乎解决了这个问题 - 谢谢!
猜你喜欢
  • 2010-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-20
  • 1970-01-01
  • 2012-10-24
  • 2016-09-18
相关资源
最近更新 更多