如何在 python-docx 中声明新的 oxml/xmlchemy 标签?
How do I declare new oxml/xmlchemy tags in python-docx?
我正在尝试将基本方程功能构建到 python-docx 中以将公式输出到 docx 文件。有人可以查看在 oxml 中注册新 class 的标准操作程序吗?查看源代码,标签似乎是通过创建复杂类型 class
来声明的
class CT_P(BaseOxmlElement):
"""
''<w:p>'' element, containing the properties and text for a paragraph.
"""
pPr = ZeroOrOne('w:pPr')
r = ZeroOrMore('w:r')
然后使用 register_element_cls() 函数注册它
from .text.paragraph import CT_P
register_element_cls('w:p', CT_P)
一些 classes 包括其他方法,但很多没有,所以看起来最小的工作示例是这样的:
from docx import Document
from docx.oxml.xmlchemy import BaseOxmlElement, ZeroOrOne, ZeroOrMore, OxmlElement
import docx.oxml
docx.oxml.ns.nsmap['m'] = ('http://schemas.openxmlformats.org/officeDocument/2006/math')
class CT_OMathPara(BaseOxmlElement):
r = ZeroOrMore('w:r')
docx.oxml.register_element_cls('m:oMathPara',CT_OMathPara)
p = CT_OMathPara()
(注意,我必须声明 m 命名空间,因为它没有在包中使用)。不幸的是,这对我根本不起作用。如果我在上面的例子中声明一个新的 class 派生,然后检查,例如,这个新的 class 的 __repr__
,它会导致异常
>> p
File "C:\ProgramData\Anaconda3\lib\site-packages\docx\oxml\ns.py", line 50, in from_clark_name
nsuri, local_name = clark_name[1:].split('}')
ValueError: not enough values to unpack (expected 2, got 1)
发生这种情况是因为我的 class 中的标签与从 python-docx 包
创建的 w:p 标签非常不同
>> paragraph._element.tag
'{http://schemas.openxmlformats.org/wordprocessingml/2006/main}p'
>> p.tag
'CT_OMathPara'
但我不知道这是为什么。通过源代码的文件搜索没有显示 CT_P class 的任何其他提及,所以我有点难过。
我认为错误来自 'm' 名称空间前缀 (nspfx) 不存在于 docx.oxml.ns.pfxmap
字典中。命名空间需要双向查找(从 nspfx 到命名空间 url 和 从 url 到 nspfx)。
所以要从 "outside" 添加新的命名空间,这意味着在加载 ns
模块之后,您需要同时执行这两项操作(如果您要修补 ns
模块直接编码,第二步将在加载时自动处理):
nsmap, pfxmap = docx.oxml.ns.nsmap, docx.oxml.ns.pfxmap
nsmap['m'] = 'http://schemas.openxmlformats.org/officeDocument/2006/math'
pfxmap['http://schemas.openxmlformats.org/officeDocument/2006/math'] = 'm'
这应该可以帮助您解决所遇到的错误,但还有更多内容需要了解。
CT_OMathPara
class 是所谓的 自定义元素 class 的示例。这意味着 lxml
为每个具有注册标签 (m:oMathPara
) 的元素实例化此 class 的对象,而不是通用的 lxml
_Element
class.
关键是,你需要让 lxml
进行构造 ,这在它解析 XML 时发生。您无法通过自己构造 class 来获得有意义的对象。
创建新 "loose" 元素(不在 XML 文档树中)的最简单方法是使用 docx.oxml.OxmlElement()
:
oMathPara = OxmlElement('m:oMathPara')
但更常见的是,docx.oxml.parse_xml()
函数用于解析整个 XML 片段。需要将解析器配置为使用自定义元素 classes,并且这些元素必须向解析器注册,因此当 oxml
模块中的元素时,您可能不想自己这样做会照顾所有需要的人。
所以通常,要获得 CT_OMathPara
的实例,您只需打开一个包含 m:oMathPara
元素的 docx(在注册新的命名空间和自定义元素 class 之后),但您也可以只解析 XML 片段。如果您在 oxml
模块中搜索 parse_xml
,您会找到大量示例。您需要在您提供的 XML 顶部获取名称空间声明,这可能有点棘手,但如果您愿意,您当然可以在文本中拼出整个 XML 片段,它只是有点冗长。
我正在尝试将基本方程功能构建到 python-docx 中以将公式输出到 docx 文件。有人可以查看在 oxml 中注册新 class 的标准操作程序吗?查看源代码,标签似乎是通过创建复杂类型 class
来声明的class CT_P(BaseOxmlElement):
"""
''<w:p>'' element, containing the properties and text for a paragraph.
"""
pPr = ZeroOrOne('w:pPr')
r = ZeroOrMore('w:r')
然后使用 register_element_cls() 函数注册它
from .text.paragraph import CT_P
register_element_cls('w:p', CT_P)
一些 classes 包括其他方法,但很多没有,所以看起来最小的工作示例是这样的:
from docx import Document
from docx.oxml.xmlchemy import BaseOxmlElement, ZeroOrOne, ZeroOrMore, OxmlElement
import docx.oxml
docx.oxml.ns.nsmap['m'] = ('http://schemas.openxmlformats.org/officeDocument/2006/math')
class CT_OMathPara(BaseOxmlElement):
r = ZeroOrMore('w:r')
docx.oxml.register_element_cls('m:oMathPara',CT_OMathPara)
p = CT_OMathPara()
(注意,我必须声明 m 命名空间,因为它没有在包中使用)。不幸的是,这对我根本不起作用。如果我在上面的例子中声明一个新的 class 派生,然后检查,例如,这个新的 class 的 __repr__
,它会导致异常
>> p
File "C:\ProgramData\Anaconda3\lib\site-packages\docx\oxml\ns.py", line 50, in from_clark_name
nsuri, local_name = clark_name[1:].split('}')
ValueError: not enough values to unpack (expected 2, got 1)
发生这种情况是因为我的 class 中的标签与从 python-docx 包
创建的 w:p 标签非常不同>> paragraph._element.tag
'{http://schemas.openxmlformats.org/wordprocessingml/2006/main}p'
>> p.tag
'CT_OMathPara'
但我不知道这是为什么。通过源代码的文件搜索没有显示 CT_P class 的任何其他提及,所以我有点难过。
我认为错误来自 'm' 名称空间前缀 (nspfx) 不存在于 docx.oxml.ns.pfxmap
字典中。命名空间需要双向查找(从 nspfx 到命名空间 url 和 从 url 到 nspfx)。
所以要从 "outside" 添加新的命名空间,这意味着在加载 ns
模块之后,您需要同时执行这两项操作(如果您要修补 ns
模块直接编码,第二步将在加载时自动处理):
nsmap, pfxmap = docx.oxml.ns.nsmap, docx.oxml.ns.pfxmap
nsmap['m'] = 'http://schemas.openxmlformats.org/officeDocument/2006/math'
pfxmap['http://schemas.openxmlformats.org/officeDocument/2006/math'] = 'm'
这应该可以帮助您解决所遇到的错误,但还有更多内容需要了解。
CT_OMathPara
class 是所谓的 自定义元素 class 的示例。这意味着 lxml
为每个具有注册标签 (m:oMathPara
) 的元素实例化此 class 的对象,而不是通用的 lxml
_Element
class.
关键是,你需要让 lxml
进行构造 ,这在它解析 XML 时发生。您无法通过自己构造 class 来获得有意义的对象。
创建新 "loose" 元素(不在 XML 文档树中)的最简单方法是使用 docx.oxml.OxmlElement()
:
oMathPara = OxmlElement('m:oMathPara')
但更常见的是,docx.oxml.parse_xml()
函数用于解析整个 XML 片段。需要将解析器配置为使用自定义元素 classes,并且这些元素必须向解析器注册,因此当 oxml
模块中的元素时,您可能不想自己这样做会照顾所有需要的人。
所以通常,要获得 CT_OMathPara
的实例,您只需打开一个包含 m:oMathPara
元素的 docx(在注册新的命名空间和自定义元素 class 之后),但您也可以只解析 XML 片段。如果您在 oxml
模块中搜索 parse_xml
,您会找到大量示例。您需要在您提供的 XML 顶部获取名称空间声明,这可能有点棘手,但如果您愿意,您当然可以在文本中拼出整个 XML 片段,它只是有点冗长。