跳转到主要内容

使用Unicode和命名空间在XML中生成大量输出

项目描述

洛迅是一个Python模块,用于使用Unicode和命名空间在XML中生成大量输出。当然,您也可以使用它生成带有纯8位字符串和没有命名空间的较小XML输出。

洛迅的特点包括

  • 小内存占用:文档通过写入输出流即时创建,无需将其全部保存在内存中。

  • 易于使用命名空间:只需添加一个命名空间,然后使用标准的 namespace:tag 语法引用它。

  • 混合Unicode和io.BytesIO:将Unicode或纯8位字符串传递给任何方法。洛迅内部将它们转换为Unicode,因此一旦参数被API接受,您就可以依赖它不会引起任何混乱的 UnicodeError 问题。

  • 自动转义:在编写文本和属性值时,无需手动处理特殊字符,如 <&

  • 健壮性:在编写文档时,会对您的所有操作进行合理性检查。许多愚蠢的错误会导致立即抛出 XmlError,例如缺少结束标签或引用未声明的命名空间。

  • 开源:遵循 GNU Lesser General Public License 3 或更高版本进行分发。

以下是一个非常基本的示例。首先,您需要创建一个输出流。在许多情况下,这将是文件,但为了简化,我们在这里使用 io.BytesIO

>>> from __future__ import unicode_literals
>>> import io
>>> out = io.BytesIO()

然后,您可以创建一个 XmlWriter 来写入此输出。

>>> xml = XmlWriter(out)

现在写入内容。

>>> xml.addNamespace("xhtml", "http://www.w3.org/1999/xhtml")
>>> xml.startTag("xhtml:html")
>>> xml.startTag("xhtml:body")
>>> xml.text("Hello world!")
>>> xml.tag("xhtml:img", {"src": "smile.png", "alt": ":-)"})
>>> xml.endTag()
>>> xml.endTag()
>>> xml.close()

结果是

>>> print out.getvalue().rstrip("\r\n")
<?xml version="1.0" encoding="utf-8"?>
<xhtml:html xmlns:xhtml="http://www.w3.org/1999/xhtml">
  <xhtml:body>
    Hello world!
    <xhtml:img alt=":-)" src="smile.png" />
  </xhtml:body>
</xhtml:html>

编写简单文档

以下示例创建一个非常简单的 XHTML 文档。

为了简化,输出将发送到 BytesIO,但您也可以使用使用 io.open(filename, "wb") 创建的二进制文件。

>>> from __future__ import unicode_literals
>>> import io
>>> out = io.BytesIO()

首先创建一个 XmlWriter 来将 XML 代码写入指定的输出。

>>> xml = XmlWriter(out)

这将自动添加 XML 前置声明。

>>> print out.getvalue().rstrip("\r\n")
<?xml version="1.0" encoding="utf-8"?>

接下来添加 <html> 开始标签。

>>> xml.startTag("html")

现在来 <body>。要传递属性,请在字典中指定它们。所以为了添加

<body id="top">

使用

>>> xml.startTag("body", {"id": "top"})

让我们添加一些文本,以便有东西可以查看。

>>> xml.text("Hello world!")

总结:关闭所有元素和文档。

>>> xml.endTag()
>>> xml.endTag()
>>> xml.close()

我们得到的结果是

>>> print out.getvalue().rstrip("\r\n")
<?xml version="1.0" encoding="utf-8"?>
<html>
  <body id="top">
    Hello world!
  </body>
</html>

指定属性

首先创建一个编写器

>>> import io
>>> out = io.BytesIO()
>>> xml = XmlWriter(out)

现在写入内容。

>>> xml.tag("img", {"src": "smile.png", "alt": ":-)"})

属性值不必是字符串,其他类型将使用 Python 的 unicode() 函数转换为 Unicode。

>>> xml.tag("img", {"src": "wink.png", "alt": ";-)", "width": 32, "height": 24})

结果是

>>> print out.getvalue().rstrip("\r\n")
<?xml version="1.0" encoding="utf-8"?>
<img alt=":-)" src="smile.png" />
<img alt=";-)" height="24" src="wink.png" width="32" />

使用命名空间

现在进行与上面相同的事情,但使用命名空间。首先创建前置声明和标题,如上所述。

>>> out = io.BytesIO()
>>> xml = XmlWriter(out)

接下来添加命名空间。

>>> xml.addNamespace("xhtml", "http://www.w3.org/1999/xhtml")

现在元素可以使用有资格的标签名,使用冒号 (:) 分隔命名空间和标签名。

>>> xml.startTag("xhtml:html")
>>> xml.startTag("xhtml:body")
>>> xml.text("Hello world!")
>>> xml.endTag()
>>> xml.endTag()
>>> xml.close()

结果,标签名现在以前缀“xhtml:”开头。

>>> print out.getvalue().rstrip("\r\n")
<?xml version="1.0" encoding="utf-8"?>
<xhtml:html xmlns:xhtml="http://www.w3.org/1999/xhtml">
  <xhtml:body>
    Hello world!
  </xhtml:body>
</xhtml:html>

处理非 ASCII 字符

有时您想使用 ASCII 范围之外的字符,例如德语的变音符号、欧元符号或日文的汉字。最简单且性能最优的方法是使用 Unicode 字符串。例如

>>> import io
>>> out = io.BytesIO()
>>> xml = XmlWriter(out, prolog=False)
>>> xml.text(u"The price is \u20ac 100") # Unicode of Euro symbol
>>> out.getvalue().rstrip("\r\n")
'The price is \xe2\x82\xac 100'

注意传递给 XmlWriter.text() 的字符串前面的“u”,它声明该字符串是一个可以包含任何字符的 Unicode 字符串,甚至包括 8 位范围之外的字符。

请注意,在输出中,欧元符号看起来与输入非常不同。这是因为输出编码是 UTF-8(默认值),它具有优点是保持所有 ASCII 字符不变,并将任何具有 128 或更高代码的字符转换为适合二进制文件或 io.BytesIO 输出流的 8 位字节序列。

如果您必须坚持经典的 8 位字符串参数,Loxun 尝试将它们转换为 Unicode。默认情况下,它假定 ASCII 编码,这在使用范围之外的字符时不会奏效。

>>> import io
>>> out = io.BytesIO()
>>> xml = XmlWriter(out, prolog=False)
>>> xml.text("The price is \xa4 100") # ISO-8859-15 code of Euro symbol
Traceback (most recent call last):
    ...
UnicodeDecodeError: 'ascii' codec can't decode byte 0xa4 in position 13: ordinal not in range(128)

在这种情况下,您必须通过指定 sourceEncoding 来告诉编写器您使用的编码。

>>> import io
>>> out = io.BytesIO()
>>> xml = XmlWriter(out, prolog=False, sourceEncoding="iso-8859-15")

现在一切又都正常了。

>>> xml.text("The price is \xa4 100") # ISO-8859-15 code of Euro symbol
>>> out.getvalue().rstrip("\r\n")
'The price is \xe2\x82\xac 100'

当然,在实际操作中,您不会用十六进制代码传递文本。相反,您只需使用 PEP 263 中描述的机制指定源编码。

格式化和缩进

默认情况下,Loxun 会为每个 startTag 开启新行,并用两个空格缩进内容。您可以将其更改为任意数量的空格或制表符。

>>> out = io.BytesIO()
>>> xml = XmlWriter(out, indent="    ") # <-- Indent with 4 spaces.
>>> xml.startTag("html")
>>> xml.startTag("body")
>>> xml.text("Hello world!")
>>> xml.endTag()
>>> xml.endTag()
>>> xml.close()
>>> print out.getvalue().rstrip("\r\n")
<?xml version="1.0" encoding="utf-8"?>
<html>
    <body>
        Hello world!
    </body>
</html>

您可以使用 pretty=False 禁用格式化输出,结果将是一个长行输出。

>>> out = io.BytesIO()
>>> xml = XmlWriter(out, pretty=False) # <-- Disable pretty printing.
>>> xml.startTag("html")
>>> xml.startTag("body")
>>> xml.text("Hello world!")
>>> xml.endTag()
>>> xml.endTag()
>>> xml.close()
>>> print out.getvalue().rstrip("\r\n")
<?xml version="1.0" encoding="utf-8"?><html><body>Hello world!</body></html>

修改 XML 前置声明

当创建一个写入器时,它会自动将一个 XML 前置处理指令写入输出。这是默认前置声明的样子。

>>> import io
>>> out = io.BytesIO()
>>> xml = XmlWriter(out)
>>> print out.getvalue().rstrip("\r\n")
<?xml version="1.0" encoding="utf-8"?>

您可以更改版本或编码。

>>> out = io.BytesIO()
>>> xml = XmlWriter(out, encoding=u"ascii", version=u"1.1")
>>> print out.getvalue().rstrip("\r\n")
<?xml version="1.1" encoding="ascii"?>

要完全省略前置声明,请设置参数 prolog=False

>>> out = io.BytesIO()
>>> xml = XmlWriter(out, prolog=False)
>>> out.getvalue()
''

添加其他内容

除了文本和标签外,XML 还提供了一些可以添加到文档中的其他内容。以下是一个使用 Loxun 实现此操作的示例。

首先,创建一个写入器。

>>> import io
>>> out = io.BytesIO()
>>> xml = XmlWriter(out)

让我们添加一个文档类型定义。

>>> xml.raw("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" SYSTEM \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">")
>>> xml.newline()

请注意,Loxun 使用通用的 XmlWriter.raw() 来完成此操作,这允许添加任何内容而无需验证或转义。您可以使用 raw() 执行各种可能导致无效 XML 的操作,但这是一种合理的用途。

接下来,让我们添加一条注释。

>>> xml.comment("Show case some rarely used XML constructs")

这是一个处理指令。

>>> xml.processingInstruction("xml-stylesheet", "href=\"default.css\" type=\"text/css\"")

最后是一个 CDATA 部分。

>>> xml.cdata(">> this will not be parsed <<")

结果是

>>> print out.getvalue().rstrip("\r\n")
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!-- Show case some rarely used XML constructs -->
<?xml-stylesheet href="default.css" type="text/css"?>
<![CDATA[>> this will not be parsed <<]]>

优化

Loxun 会自动优化成对的空起始/结束标签。例如

>>> out = io.BytesIO()
>>> xml = XmlWriter(out)
>>> xml.startTag("customers")
>>> xml.startTag("person", {"id": "12345", "name": "Doe, John"})
>>> xml.endTag("person") # without optimization, this would add </person>.
>>> xml.endTag()
>>> xml.close()
>>> print out.getvalue().rstrip("\r\n")
<?xml version="1.0" encoding="utf-8"?>
<customers>
  <person id="12345" name="Doe, John" />
</customers>

尽管有显式的 startTag("person") 和匹配的 endtag(),输出中仅包含简单的 <person ... /> 标签。

贡献

如果您想帮助改进 Loxun,您可以在 http://github.com/roskakori/loxun 处访问源代码。

未来

目前,Loxun 正在执行其构建的目的。

目前没有计划在近期内改进它,但以下是一些可能在某个时候添加的功能列表

  • 添加对标签和属性名称的验证,以确保所有使用的字符都是允许的。例如,目前 Loxun 对名为“a#b*c$d_”的标签不进行抱怨。

  • 当使用属性而不是 XmlWriter.addNamespace() 添加命名空间时,引发 XmlError

  • 添加日志支持以简化调用代码的调试。可能 XmlWriter 将获得一个属性 logger,它是一个标准的 logging.Logger。默认情况下,它可以记录 Loxun 转换为 XmlError 的原始异常以及打开和关闭的命名空间。将日志级别更改为 logging.DEBUG 将记录每个标签和 XML 构造,包括有关内部标签堆栈的附加信息。这样,您可以动态地增加或减少日志输出。

  • 重新考虑格式化输出。而不是只能在使用 XmlWriter 初始化时设置的全球属性,它可以是 XmlWriter.startTag() 的可选参数,可以根据需要打开和关闭。属性名称可以是 literal 而不是 pretty(具有相反的逻辑)。

  • 添加一个 DomWriter,用于创建 xml.dom.minidom.Document

一些其他 XML 库支持的特性,但我从未看到过任何真正的用途

  • 指定标签的属性顺序。

版本历史

版本 2.0,2014-07-28

  • 添加了对 Python 3.2+ 的支持,同时保留了使用 Python 2.6+ 运行的选项(问题 #5;感谢 Stefan Schwarzer 在 EuroPython 2014 的“Python 2 到 3”冲刺期间提供的指导)。

  • 已取消对 Python 2.5 的支持,如果您被困在这个版本中,请继续使用 Loxun 1.3。

版本 1.3,2012-01-01

  • 添加了 endTags() 以关闭一个或多个打开的标签(问题 #3,由 Anton Kolechkin 贡献)。

  • 添加了与XmlWriter类似的ChainXmlWriter,允许链式调用方法,从而编写更简洁的源代码(问题编号 #3,由Anton Kolechkin 贡献)。

版本 1.2,2011-03-12

  • 修复了当设置XmlWriter(..., encoding=...)时出现的AttributeError

版本 1.1,08-Jan-2011

  • 修复了当将pretty设置为False时出现的AssertionError(问题编号 #1;由David Cramer修复)。

版本 1.0,11-Oct-2010

版本 0.8,11-Jul-2010

  • 添加了将属性传递给XmlWriter.startTag()XmlWriter.tag()的功能,其值可以是除了strunicode之外的其他类型。当写入XML时,使用Python的内置unicode()函数进行转换。

  • 添加了从发行版中缺失的一些文件,最重要的是测试套件。

版本 0.7,03-Jul-2010

  • 添加了对匹配不带内容的中间标签的优化。例如,x.startTag("some"); x.endTag()的结果是<some />而不是<some></some>

  • 修复了对未知名称空间的处理。现在它们会引发一个XmlError而不是ValueError

版本 0.6,03-Jun-2010

  • 添加了indent选项,用于指定每行开始的缩进文本。

  • 添加了newline选项,用于指定如何结束写入的行。

  • 修复了XmlWriter.tag()没有删除立即在其之前声明的名称空间的问题。

  • 整理了文档。

版本 0.5,25-May-2010

  • 修复了名称空间属性名称中的拼写错误。

  • 修复了在调用XmlWriter.tag()之前添加名称空间,导致出现XmlError的问题。

版本 0.4,21-May-2010

  • 添加了sourceEncoding选项,简化了经典字符串的处理。手动部分“处理非ASCII字符”解释了如何使用它。

版本 0.3,17-May-2010

  • 添加了作用域名称空间,它们由XmlWriter.endTag()自动删除。

  • text()更改为在启用美化打印时规范化换行符和空白。

  • 将XML声明写入构造函数,并删除了XmlWriter.prolog()。要省略声明,在创建XmlWriter时指定prolog=False。如果您以后想自己写入声明,请使用XmlWriter.processingInstruction()

  • *Element()重命名为*Tag,因为它们实际上只写入标签,而不是整个元素。

版本 0.2,16-May-2010

  • 添加了XmlWriter.comment()XmlWriter.cdata()XmlWriter.processingInstruction(),用于写入这些特定的XML结构。

  • 添加了缩进和自动换行,如果启用美化打印,则应用于文本。

  • 如果禁用美化打印,则从声明中删除换行。

  • 修复了声明中缺失的“?”。

版本 0.1,15-May-2010

  • 首次发布。

项目详情


下载文件

下载适合您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。

源分发

loxun-2.0.zip (27.8 kB 查看散列值)

上传时间

支持