繁体   English   中英

在lxml中定义默认名称空间(无前缀)

[英]Define default namespace (unprefixed) in lxml

使用lxml渲染XHTML时,一切都很好,除非您碰巧使用Firefox,而Firefox似乎无法处理以名称空间为前缀的XHTML元素和javascript。 尽管Opera可以执行javascript(这对jQuery和MathJax都适用)很好,但是无论XHTML名称空间是否带有前缀(在我的情况下为h: :),在Firefox中,脚本都会中止,并出现奇怪的错误( this.head对于MathJax, 则未定义this.head )。

我知道register_namespace函数,但是它既不接受None也不接受""作为命名空间前缀。 我听说_namespace_maplxml.etree模块,但我的Python抱怨说,这个属性不存在(版本问题?)

是否还有其他方法可以删除XHTML名称空间的名称空间前缀? 请注意,正如在另一个相关问题的答案中所建议的那样, str.replace 不是我可以接受的方法,因为它不了解XML语义,并且很容易弄乱生成的文档。

根据请求,您将找到两个可供使用的示例。 一个具有名称空间前缀一个没有 第一个将在Firefox中显示0(错误),第二个将显示1(正确)。 Opera将使两者都正确。 显然这是Firefox的错误 ,但这仅是希望使用lxml无前缀XHTML的理由–还有其他充分的理由可以减少移动客户端的流量,等等(甚至h:如果您考虑html的数十或数百字,这也很多。标签)。

使用ElementMakerElementMaker提供一个nsmap ,将None映射到您的默认命名空间。

#!/usr/bin/env python
# dogeml.py

from lxml.builder import ElementMaker
from lxml import etree

E = ElementMaker(
    nsmap={
        None: "http://wow/"    # <--- This is the special sauce
    }
)

doge = E.doge(
    E.such('markup'),
    E.many('very namespaced', syntax="tricks")
)

options = {
    'pretty_print': True,
    'xml_declaration': True,
    'encoding': 'UTF-8',
}

serialized_bytes = etree.tostring(doge, **options)
print(serialized_bytes.decode(options['encoding']))

正如您在此脚本的输出中所看到的,定义了默认命名空间,但标签没有前缀。

<?xml version='1.0' encoding='UTF-8'?>
<doge xmlns="http://wow/">
   <such>markup</such>
   <many syntax="tricks">very namespaced</many>
</doge>

我已经使用Python 2.7.6,3.3.5和3.4.0以及lxml 3.3.1测试了这段代码。

此XSL转换content删除所有前缀,同时保留在根节点中定义的名称空间:

import lxml.etree as ET

content = '''\
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE html>
<h:html xmlns:h="http://www.w3.org/1999/xhtml" xmlns:ml="http://foo">
  <h:head>
    <h:title>MathJax Test Page</h:title>
    <h:script type="text/javascript"><![CDATA[
      function test() {
        alert(document.getElementsByTagName("p").length);
      };
    ]]></h:script>
  </h:head>
  <h:body onload="test();">
    <h:p>test</h:p>
    <ml:foo></ml:foo>
  </h:body>
</h:html>
'''
dom = ET.fromstring(content)

xslt = '''\
<xsl:stylesheet version="1.0"
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="no"/>

<!-- identity transform for everything else -->
<xsl:template match="/|comment()|processing-instruction()|*|@*">
    <xsl:copy>
      <xsl:apply-templates />
    </xsl:copy>
</xsl:template>

<!-- remove NS from XHTML elements -->
<xsl:template match="*[namespace-uri() = 'http://www.w3.org/1999/xhtml']">
    <xsl:element name="{local-name()}">
      <xsl:apply-templates select="@*|node()" />
    </xsl:element>
</xsl:template>

<!-- remove NS from XHTML attributes -->
<xsl:template match="@*[namespace-uri() = 'http://www.w3.org/1999/xhtml']">
    <xsl:attribute name="{local-name()}">
      <xsl:value-of select="." />
    </xsl:attribute>
</xsl:template>
</xsl:stylesheet>
'''

xslt_doc = ET.fromstring(xslt)
transform = ET.XSLT(xslt_doc)
dom = transform(dom)

print(ET.tostring(dom, pretty_print = True, 
                  encoding = 'utf-8'))

产量

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>MathJax Test Page</title>
    <script type="text/javascript">
      function test() {
        alert(document.getElementsByTagName("p").length);
      };
    </script>
  </head>
  <body onload="test();">
    <p>test</p>
    <ml:foo xmlns:ml="http://foo"/>
  </body>
</html>

为了扩展@neirbowj的答案,但使用ET.Element和ET.SubElement并使用混合名称空间呈现文档,其中根恰好是显式命名空间,而子元素( channel )是默认名称空间:

# I set up but don't use the default namespace:
root = ET.Element('{http://www.w3.org/1999/02/22-rdf-syntax-ns#}RDF', nsmap={None: 'http://purl.org/rss/1.0/'})
# I use the default namespace by including its URL in curly braces:
e = ET.SubElement(root, '{http://purl.org/rss/1.0/}channel')
print(ET.tostring(root, xml_declaration=True, encoding='utf8').decode())

这将打印出以下内容:

<?xml version='1.0' encoding='utf8'?>
<rdf:RDF xmlns="http://purl.org/rss/1.0/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><channel/></rdf:RDF>

它自动将rdf用于RDF名称空间。 我不确定它是如何计算出来的。 如果我想指定它,我可以将它添加到根元素中的nsmap:

nsmap = {None: 'http://purl.org/rss/1.0/',
         'doge': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'}
root = ET.Element('{http://www.w3.org/1999/02/22-rdf-syntax-ns#}RDF', nsmap=nsmap)
e = ET.SubElement(root, '{http://purl.org/rss/1.0/}channel')
print(ET.tostring(root, xml_declaration=True, encoding='utf8').decode())

......我明白了:

<?xml version='1.0' encoding='utf8'?>
<doge:RDF xmlns:doge="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://purl.org/rss/1.0/"><channel/></doge:RDF>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM