python xml.etree.ElementTree tostring() fromstring() 在unicode码点上回合失败

0 投票
2 回答
6606 浏览
提问于 2025-04-18 15:26

我在用 Python 2.7 的 xml.etree.ElementTree 处理 XML 时,遇到了从字符串转换回去的问题。如果树里有非 ASCII 的 Unicode 字符,调用 ET.fromstring() 来处理 ET.tostring() 就会失败。

为什么会这样呢?既然 ElementTree 需要字节流并且自己处理解码,那它为什么默认使用 ASCII 解析器呢?这是不是跟我忽略的某些东西有关,比如 Python 文件的编码或者地区设置?

  1. 只有 ASCII 字符可以正常工作:

    import xml.etree.ElementTree as ET
    
    t1 = ET.Element('test')
    t1.text = u'hello world'
    t1_roundtrip = ET.fromstring(ET.tostring(t1, encoding='utf8', method='xml'))
    # ET.dump(t1) == ET.dump(t1_roundtrip)
    
  2. Unicode 代码点会失败:

    import xml.etree.ElementTree as ET
    
    t2 = ET.Element('test')
    t2.text = u'\u2603'
    t2_roundtrip = ET.fromstring(ET.tostring(t2, encoding='utf8', method='xml'))
    
    >>> t2_roundtrip = ET.fromstring(ET.tostring(t2, encoding='utf8', method='xml'))
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/opt/rh/python27/root/usr/lib64/python2.7/xml/etree/ElementTree.py", line 1300, in XML
        parser.feed(text)
      File "/opt/rh/python27/root/usr/lib64/python2.7/xml/etree/ElementTree.py", line 1642, in feed
        self._raiseerror(v)
      File "/opt/rh/python27/root/usr/lib64/python2.7/xml/etree/ElementTree.py", line 1506, in _raiseerror
        raise err
    xml.etree.ElementTree.ParseError: not well-formed (invalid token): line 2, column 6
    

2 个回答

0

我找到了解决这个问题的两种方法:

  1. 在使用 tostring() 时,不要指定编码:

    import xml.etree.ElementTree as ET
    t3 = ET.Element('test')
    t3.text = u'\u2603'
    t3_roundtrip = ET.fromstring(ET.tostring(t3, method='xml'))
    
  2. 使用 utf-8 编码来指定 XMLParser

    import xml.etree.ElementTree as ET
    t4 = ET.Element('test')
    t4.text = u'\u2603'
    t4_roundtrip_utf = ET.fromstring(
        ET.tostring(t3, encoding='utf8', method='xml'),
        parser=ET.XMLParser(encoding='utf-8'))
    

那我为什么需要这两种方法中的任意一种呢?难道 XML 文件不是默认就是 utf-8 编码吗?

2

你指定了一个不合法的编码。引用一下ElementTree 的文档

在 XML 输出中包含的编码字符串应该符合相关标准。例如,“UTF-8”是有效的,但“UTF8”就不行。可以查看这个链接这个链接了解更多信息。

撰写回答