5

我曾经ElementTree生成带有特殊字符'\x0b'的xml,然后minidom用来解析它。它会抛出not well-formed错误。

import xml.etree.ElementTree as ET
from xml.dom import minidom
root = ET.Element('root')
root.text='\x0b'
xml = ET.tostring(root, 'UTF-8')
print(xml)
pretty_tree = minidom.parseString(xml)

生成的 XML<root>\x0b</root>

错误

Traceback (most recent call last):
  File "testXml.py", line 7, in <module>
    pretty_tree = minidom.parseString(xml)
  File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/xml/dom/minidom.py", line 1968, in parseString
    return expatbuilder.parseString(string)
  File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/xml/dom/expatbuilder.py", line 925, in parseString
    return builder.parseString(string)
  File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/xml/dom/expatbuilder.py", line 223, in parseString
    parser.Parse(string, True)
xml.parsers.expat.ExpatError: not well-formed (invalid token): line 1, column 6
4

3 回答 3

6

此行为在过去已作为错误提出并解决为“无法修复”。

ElementTree 模块的作者评论

对于 ET,[这种行为] 非常有目的。验证每个应用程序提供的数据会降低所有应用程序的性能,即使只有一小部分人会尝试序列化无法用 XML 表示的数据。

最后的评论(由lxml的维护者,也是 Python 核心开发者)包括以下观察:

这是一个棘手的决定。例如,lxml 验证用户输入,但那是因为它无论如何都必须处理它并且直接在输入上进行处理(并且在 C 代码中非常有效)。另一方面,ET 对允许用户执行的操作相当宽松,并且不会对用户输入进行太多处理。它甚至允许在处理过程中使用无效树,并且仅在请求序列化它时才期望树是可序列化的。

我认为这是一种公平的行为,因为大多数用户输入都可以,并且不需要遭受验证所有输入的性能损失。例如,空字符在文本中是非常罕见的,我认为让用户自己处理可能出现的少数情况是合理的。

...

最后,真正关心正确输出的用户应该在序列化对其进行某种模式验证,因为这不仅会检测数据问题,还会检测结构和逻辑问题(例如缺失或空属性),特别是针对他们的目标数据格式。在某些情况下,它甚至可能检测到由于服务器机器中旧的非 ECC RAM 导致的随机数据损坏。:)

...

所以总而言之,ET.tostring会生成格式不正确的xml,这是设计使然。如有必要,可以使用ET.fromstring或其他解析器解析输出以检查其格式是否正确。或者,可以使用 lxml 代替 ElementTree。

于 2020-05-30T08:37:04.613 回答
1

作为我自己的一种解决方法,我编写了一个辅助方法来清除受限字符,然后再保存到 XML 模型:

def clean(str):
  return re.sub(r'[^\u0009\u000A\u000D\u0020-\uD7FF\uE000-\uFFFD\u10000-\u10FFF]+', '', str)
于 2020-05-30T15:42:57.167 回答
1

\x0b是一个 XML 限制字符。此问题的答案中对有效字符和受限字符进行了很好的描述。

于 2020-05-30T03:44:56.890 回答