2013-07-19 76 views
1

我正在将XML文档转换为HTML。需要发生的一件事是删除名称空间,这些名称空间不能在HTML中合法声明(除非它是根标记中的XHTML名称空间)。我已经在5-10年前发现了关于XML :: LibXML和LibXML2的难度,但最近并没有这么多。这里有一个例子:使用XML删除XML名称空间:: LibXML

use XML::LibXML; 
use XML::LibXML::XPathContext; 
use feature 'say'; 

my $xml = <<'__EOI__'; 
<myDoc> 
    <par xmlns:bar="www.bar.com"> 
    <bar:foo/> 
    </par> 
</myDoc> 
__EOI__ 

my $parser = XML::LibXML->new(); 
my $doc = $parser->parse_string($xml); 

my $bar_foo = do{ 
    my $xpc = XML::LibXML::XPathContext->new($doc); 
    $xpc->registerNs('bar', 'www.bar.com'); 
    ${ $xpc->findnodes('//bar:foo') }[0]; 
}; 
$bar_foo->setNodeName('foo'); 
$bar_foo->setNamespace('',''); 
say $bar_foo->nodeName; #prints 'bar:foo'. Dang! 

my @namespaces = $doc->findnodes('//namespace::*'); 
for my $ns (@namespaces){ 
    # $ns->delete; #can't find any such method for namespaces 
} 
say $doc->toStringHTML; 

在这段代码中,我尝试了一些不起作用的东西。首先,我尝试将bar:foo元素的名称设置为前缀foo(文档说该方法知道命名空间,但显然不是)。然后我尝试将元素名称空间设置为null,并且这也不起作用。最后,我查看了文档中有关删除名称空间的方法。没有这样的运气。最终的输出字符串仍然具有我想要移除的所有内容(名称空间声明和前缀)。

有没有人有办法删除名称空间,将元素和属性设置为空名称空间?

+0

你正试图改变元素的命名空间,但你不迭代元素? – ikegami

+0

@ikegami我会的,但我想先看看它在其中一个上工作。 –

回答

3

这是我自己的体操答案。如果没有更好的办法,它会做的。我当然希望有更好的办法...

replace_without_ns方法只是复制没有命名空间的节点。相反,任何需要命名空间的子元素都会获得对它们的声明。下面的代码将整个文档进空命名空间:

use strict; 
use warnings; 
use XML::LibXML; 

my $xml = <<'__EOI__'; 
<myDoc xmlns="foo"> 
    <par xmlns:bar="www.bar.com" foo="bar"> 
    <bar:foo stuff="junk"> 
     <baz bar:thing="stuff"/> 
     fooey 
     <boof/> 
    </bar:foo> 
    </par> 
</myDoc> 
__EOI__ 

my $parser = XML::LibXML->new(); 
my $doc = $parser->parse_string($xml); 

# remove namespaces for the whole document 
for my $el($doc->findnodes('//*')){ 
    if($el->getNamespaces){ 
     replace_without_ns($el); 
    } 
} 

# replaces the given element with an identical one without the namespace 
# also does this with attributes 
sub replace_without_ns { 
    my ($el) = @_; 
    # new element has same name, minus namespace 
    my $new = XML::LibXML::Element->new($el->localname); 
    #copy attributes (minus namespace namespace) 
    for my $att($el->attributes){ 
     if($att->nodeName !~ /xmlns(?::|$)/){ 
      $new->setAttribute($att->localname, $att->value); 
     } 
    } 
    #move children 
    for my $child($el->childNodes){ 
     $new->appendChild($child); 
    } 

    # if working with the root element, we have to set the new element 
    # to be the new root 
    my $doc = $el->ownerDocument; 
    if($el->isSameNode($doc->documentElement)){ 
     $doc->setDocumentElement($new); 
     return; 
    } 
    #otherwise just paste the new element in place of the old element 
    $el->parentNode->insertAfter($new, $el); 
    $el->unbindNode; 
    return; 
} 

print $doc->toStringHTML; 
+0

对不起,我不记得了。这是很久以前的事了;要么我不知道这么做,要么阻止了我使用这个模块的内存问题之一。我记得在试图保留我仍在使用的东西被解除分配时遇到了麻烦。 –

+0

@ikegami'unbindNode'在最后一次引用被删除后也会释放节点的内存。 – nwellnhof

+0

@ikegami否,即使原始文档仍然存在,使用“unbindNode”或“removeChild”删除的节点也可以被释放。他们被转移到一个带有内部引用的隐藏文档片段。此文档片段引用原始文档,但如果其引用计数下降到零,则文档片段[将被释放](http://www.bytebucket.org/shlomif/perl-xml-libxml/src/b73be7040551c1f2c98cc3bcd2e953f7683bbef2/perl-libxml- mm.c?在默认=#CL-462)。 – nwellnhof

0

下面是使用XSLT样式表一个简单的解决方案:

use strict; 
use warnings; 
use XML::LibXML; 
use XML::LibXSLT; 

my $xml = <<'__EOI__'; 
<myDoc xmlns="foo"> 
    <par xmlns:bar="www.bar.com" foo="bar"> 
    <bar:foo stuff="junk"> 
     <baz bar:thing="stuff"/> 
     fooey 
     <boof/> 
    </bar:foo> 
    </par> 
</myDoc> 
__EOI__ 

my $parser = XML::LibXML->new(); 
my $doc = $parser->parse_string($xml); 

my $xslt = XML::LibXSLT->new(); 
my $xsl_doc = $parser->parse_string(<<'XSL'); 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:template match="*"> 
    <xsl:element name="{local-name()}"> 
     <xsl:apply-templates select="node()|@*"/> 
    </xsl:element> 
    </xsl:template> 
    <xsl:template match="@*"> 
    <xsl:attribute name="{local-name()}"> 
     <xsl:value-of select="."/> 
    </xsl:attribute> 
    </xsl:template> 
</xsl:stylesheet> 
XSL 

my $stylesheet = $xslt->parse_stylesheet($xsl_doc); 
my $result  = $stylesheet->transform($doc); 
print $stylesheet->output_as_bytes($result); 

注意,如果要复制注释或处理指令,进一步调整是需要。