如何防止DOMNode :: cloneNode()插入冗余的命名空间?

时间:2016-11-10 22:51:52

标签: php xml dom xml-namespaces

我正在使用PHP的built-in DOM实现来修改XML文档,特别是ODS电子表格中的content.xml文件。本文档大量使用命名空间(在根元素中声明了35个不同的命名空间)。

我正在尝试使用浅cloneNode()table-cell元素复制到新行,但结果与原始元素不完全相同:

<?xml version="1.0" encoding="UTF-8"?>
<office:document-content
    xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0"
    xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
    xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0"
    [... snip 32 ...]>

<!-- original -->
<table:table-cell table:style-name="ce5"
                  office:value-type="string"
                  calcext:value-type="string">

<!-- cloned -->
<table:table-cell xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0"
                  xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
                  xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0"
                  table:style-name="ce5"
                  office:value-type="string"
                  calcext:value-type="string">

虽然这在语义上相似,但它可能会导致较大的电子表格出现大量膨胀(即使XML在磁盘上压缩)。

有解决方法吗?

使用非命名空间感知方法的简单方法,以及简单地复制属性(包括前缀和标记名称)似乎最终有效:

$clone = $doc->createElement($ele->tagName);
foreach ($ele->attributes as $att) {
    $clone->setAttribute($att->nodeName, $att->value);
}

生成的XML看起来完全符合预期。但是当克隆元素再次被操纵时:

$clone->setAttributeNS($officeNS, "office:value-type", "string");

结果有两个相同的属性名称:

<table:table-cell xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
                  table:style-name="ce5"
                  office:value-type="string"
                  calcext:value-type="string"
                  office:value-type="string"
                  office:string-value="">

使文档无效。一般来说,我发现混合使用命名空间和非命名空间的方法调用是不切实际的。

1 个答案:

答案 0 :(得分:1)

这是一个libxml常量,允许在加载时优化命名空间:

$xml = <<<'XML'
<f:foo xmlns:f="urn:foo">
  <f:foo>
    <f:foo xmlns:f="urn:foo">
    </f:foo>
  </f:foo>
</f:foo>
XML;

$document = new DOMDocument();
$document->loadXml($xml, LIBXML_NSCLEAN);
echo $document->saveXml();

输出:

<?xml version="1.0"?>
<f:foo xmlns:f="urn:foo">
  <f:foo>
    <f:foo>
    </f:foo>
  </f:foo>
</f:foo>

这主要起作用,但如果在同一文档中为不同的名称空间使用相同的前缀,则会得到一些无效结果。

FluentDOM库包含此作业的优化程序。它允许您更改/定义前缀。