如何使用Python的cElementTree创建<!DOCTYPE>文档类型声明

17

我尝试使用这个问题中的答案,但无法使其起作用:如何使用 Python 的 ElementTree 创建“虚拟根”?

以下是我的代码:

import xml.etree.cElementTree as ElementTree
from StringIO import StringIO
s = '<?xml version=\"1.0\" encoding=\"UTF-8\" ?><!DOCTYPE tmx SYSTEM \"tmx14a.dtd\" ><tmx version=\"1.4a\" />'
tree = ElementTree.parse(StringIO(s)).getroot()
header = ElementTree.SubElement(tree,'header',{'adminlang': 'EN',})
body = ElementTree.SubElement(tree,'body')
ElementTree.ElementTree(tree).write('myfile.tmx','UTF-8')

当我打开生成的“myfile.tmx”文件时,它包含以下内容:

<?xml version='1.0' encoding='UTF-8'?>
<tmx version="1.4a"><header adminlang="EN" /><body /></tmx>

我错过了什么?还是有更好的工具吗?

4个回答

17
你可以在write函数上设置xml_declaration参数为False,这样输出就不会带有编码的XML声明,然后手动添加需要的头部即可。实际上,如果将编码设置为'utf-8'(小写),XML声明也不会被添加。
import xml.etree.cElementTree as ElementTree

tree = ElementTree.Element('tmx', {'version': '1.4a'})
ElementTree.SubElement(tree, 'header', {'adminlang': 'EN'})
ElementTree.SubElement(tree, 'body')

with open('myfile.tmx', 'wb') as f:
    f.write('<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE tmx SYSTEM "tmx14a.dtd">'.encode('utf8'))
    ElementTree.ElementTree(tree).write(f, 'utf-8')

生成的文件(为了可读性手动添加了换行符):

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE tmx SYSTEM "tmx14a.dtd">
<tmx version="1.4a">
    <header adminlang="EN" />
    <body />
</tmx>

你能解释一下你是如何将新行添加到 XML 中的吗? - Learner
@学习者:我手动添加了它以提高可读性。如果你想从 ElementTree 获取带有换行符的 XML,请搜索如何美化打印 XML。 - demalexx
这给我在macOS上的Python 3.6.4中产生了一个错误TypeError: write() argument must be str, not bytes. 我认为这是因为你在同一个open()命令中先写作字符串,然后再写作二进制。 - Elliott B
@ElliottB 谢谢,我更新了代码。现在应该可以在 Python 2 和 3 上运行了。 - demalexx
这个解决方案不起作用,除非你手动输入(如所述)ElementTree,这肯定不是你想要做的。我在下面提供了一个简单而愚蠢的解决方案来解决这个问题。 - Emilio Conte
@Learner 你可以在 XML 声明和文档类型之间的字符串中简单地插入 "\n"(不带引号)。 - posfan12

13
您可以使用lxml及其tostring函数:
from lxml import etree

s = """<?xml version="1.0" encoding="UTF-8"?>
<tmx version="1.4a"/>""" 

tree = etree.fromstring(s)
header = etree.SubElement(tree,'header',{'adminlang': 'EN'})
body = etree.SubElement(tree,'body')

print etree.tostring(tree, encoding="UTF-8",
                     xml_declaration=True,
                     pretty_print=True,
                     doctype='<!DOCTYPE tmx SYSTEM "tmx14a.dtd">')

=>

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE tmx SYSTEM "tmx14a.dtd">
<tmx version="1.4a">
  <header adminlang="EN"/>
  <body/>
</tmx>

1
我在Python 3.6中遇到了这个错误:ValueError: Unicode strings with encoding declaration are not supported. Please use bytes input or XML fragments without declaration. - nowox
1
使用Python 3.6,etree.fromstring(s.encode("UTF-8")) 对我有效。 - mzjn

2

我使用了不同的解决方案来添加DOCTYPE,非常简单,非常愚蠢。

import xml.etree.ElementTree as ET

with open(path_file, "w", encoding='UTF-8') as xf:
    doc_type = '<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE dlg:window ' \
               'PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "dialog.dtd">'
    tostring = ET.tostring(root).decode('utf-8')
    file = f"{doc_type}{tostring}"
    xf.write(file)

0

我也无法使用原始的ElementTree找到解决此问题的方法,而 demalexx 提出的解决方案创建了非有效的XML,被我的应用程序(DITA)拒绝。 我的建议是使用其他模块的变通方法,这对我来说非常完美。

import re
# found no way for cleanly specify a <!DOCTYPE ...> stanza in ElementTree so
# so we substitute the current <?xml ... ?> stanza with a full <?xml... + <!DOCTYPE...
new_header = '<?xml version="1.0" encoding="UTF-8" ?>\n' \
                 '<!DOCTYPE topic PUBLIC "-//OASIS//DTD DITA Topic//EN" "topic.dtd">\n'

target_xml = re.sub(u"\<\?xml .+?>", new_header, source_xml)
with open(filename, 'w') as catalog_file:
    catalog_file.write(target_xml.encode('utf8'))

你能详细说明一下“非有效XML”问题吗? - posfan12
@posfan12,我猜主要问题可能是在行首没有DTD,这在demalexx的答案中很容易修复。 - Luis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接