Aspose.Words Document Object Model (DOM)

Aspose.Words Document Object Model (DOM) 是 Word 文档的内存表示形式。 Aspose.Words DOM 允许您以编程方式读取、操作和修改 Word 文档的内容和格式。

本节介绍 Aspose.Words DOM 的主要类及其关系。通过使用 Aspose.Words DOM 类,您可以获得对文档元素和格式的编程访问。

创建 Document 对象树

当文档被读入 Aspose.Words DOM 时,就会构建一个对象树,源文档的不同类型的元素都有自己的具有各种属性的 DOM 树对象。

构建文档节点树

当 Aspose.Words 将 Word 文档读入内存时,它会创建代表各种文档元素的不同类型的对象。每一段文本、段落、表格或节都是一个节点,甚至文档本身也是一个节点。 Aspose.Words 为每个文档节点类型定义一个类。

Aspose.Words 中的文档树遵循复合设计模式:

  • 所有节点类最终都派生自 Node 类,该类是 Aspose.Words Document Object Model 中的基类。
  • 可以包含其他节点的节点(例如 SectionParagraph)派生自 CompositeNode 类,而 CompositeNode 类又派生自 Node 类。

下图显示了 Aspose.Words Document Object Model (DOM) 节点类之间的继承。抽象类的名称以斜体显示。

aspose 单词 dom

让我们看一个例子。下图显示了包含不同类型内容的 Microsoft Word 文档。

文档示例 aspose-words

将上述文档读入 Aspose.Words DOM 时,将创建对象树,如下图所示。

dom-aspose-词

DocumentSectionParagraphTableShapeRun 以及图表上的所有其他省略号都是表示 Word 文档元素的 Aspose.Words 对象。

获取 Node 类型

尽管 Node 类足以区分不同的节点,但 Aspose.Words 提供了 NodeType 枚举来简化某些 API 任务,例如选择特定类型的节点。

每个节点的类型可以使用 Node.node_type 属性获取。此属性返回 NodeType 枚举值。例如,由 Paragraph 类表示的段落节点返回 NodeType.PARAGRAPH,由 Table 类表示的表节点返回 NodeType.TABLE

以下示例演示如何使用 NodeType 枚举获取节点类型:

# For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Python-via-.NET
doc = aw.Document()
type = doc.node_type

文档树导航

Aspose.Words 将文档表示为节点树,使您能够在节点之间导航。本节介绍如何浏览和导航 Aspose.Words 中的文档树。

当您在文档资源管理器中打开前面提供的示例文档时,节点树的显示方式与 Aspose.Words 中的显示方式完全相同。

文档中文档浏览器

文档节点关系

树中的节点之间存在关系:

  • 包含另一个节点的节点是 parent.
  • 父节点包含的节点是child. 同一父节点的子节点是sibling 节点。
  • root 节点始终是 Document 节点。

可以包含其他节点的节点派生自 CompositeNode 类,所有节点最终都派生自 Node 类。这两个基类提供了用于树结构导航和修改的通用方法和属性。

下面的 UML 对象图显示了示例文档的几个节点以及它们通过父、子和同级属性相互之间的关系:

文档节点关系-aspose-词

文档是节点所有者

节点始终属于特定文档,即使它刚刚创建或从树中删除,因为重要的文档范围结构(例如样式和列表)存储在 Document 节点中。例如,不可能有没有 DocumentParagraph,因为每个段落都有一个为文档全局定义的指定样式。创建任何新节点时都会使用此规则。直接将新的 Paragraph 添加到 DOM 需要将文档对象传递给构造函数。

使用 DocumentBuilder 创建新段落时,构建器始终有一个通过 DocumentBuilder.document 属性链接到它的 Document 类。

以下代码示例显示,在创建任何节点时,始终定义拥有该节点的文档:

# For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Python-via-.NET
doc = aw.Document()
# Creating a new node of any type requires a document passed into the constructor.
para = aw.Paragraph(doc)
# The new paragraph node does not yet have a parent.
print(f"Paragraph has no parent node: {para.parent_node == None}")
# But the paragraph node knows its document.
print(f"Both nodes' documents are the same: {para.document == doc}")
# The fact that a node always belongs to a document allows us to access and modify
# properties that reference the document-wide data, such as styles or lists.
para.paragraph_format.style_name = "Heading 1"
# Now add the paragraph to the main text of the first section.
doc.first_section.body.append_child(para)
# The paragraph node is now a child of the Body node.
print(f"Paragraph has a parent node: {para.parent_node != None}")

父节点

每个节点都有一个由 parent_node 属性指定的父节点。一个节点没有父节点,即parent_nodeNone,以下情况:

  • 该节点刚刚创建,尚未添加到树中。
  • 该节点已从树中删除。
  • 这是根 Document 节点,它始终具有 None 父节点。

您可以通过调用 Node.remove 方法从其父节点中删除节点。以下代码示例展示了如何访问父节点:

# For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Python-via-.NET
doc = aw.Document()
# The section is the first child node of the document.
section = doc.first_child
# The section's parent node is the document.
print(f"Section parent is the document: {doc == section.parent_node}")

子节点

访问 CompositeNode 子节点的最有效方法是通过分别返回第一个和最后一个子节点的 first_childlast_child 属性。如果没有子节点,这些属性将返回 None

CompositeNode 还提供 get_child_nodes 集合,支持对子节点进行索引或枚举访问。 get_child_nodes 方法返回节点的实时集合,这意味着每当文档发生更改时(例如删除或添加节点时),获取子节点 集合都会自动更新。

如果节点没有子节点,则 获取子节点 方法返回一个空集合。您可以使用 has_child_nodes 属性检查 CompositeNode 是否包含任何子节点。

以下代码示例演示如何使用 获取子节点 集合提供的枚举器枚举 CompositeNode 的直接子节点:

# For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Python-via-.NET
doc = aw.Document()
paragraph = doc.get_child(aw.NodeType.PARAGRAPH, 0, True).as_paragraph()
children = paragraph.child_nodes
for child in children :
# A paragraph may contain children of various types such as runs, shapes, and others.
if child.node_type == aw.NodeType.RUN :
run = child.as_run()
print(run.text)

兄弟节点

您可以分别使用 previous_siblingnext_sibling 属性获取紧邻特定节点之前或之后的节点。如果节点是其父节点的最后一个子节点,则 next_sibling 属性为 None。相反,如果该节点是其父节点的第一个子节点,则 previous_sibling 属性为 None

下面的代码示例展示了如何高效地访问复合节点的所有直接和间接子节点:

# For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Python-via-.NET
def test_recurse_all_nodes(self) :
doc = aw.Document(docs_base.my_dir + "Paragraphs.docx")
# Invoke the recursive function that will walk the tree.
self.traverse_all_nodes(doc)
# <summary>
# A simple function that will walk through all children of a specified node recursively
# and print the type of each node to the screen.
# </summary>
def traverse_all_nodes(self, parentNode) :
# This is the most efficient way to loop through immediate children of a node.
for childNode in parentNode.child_nodes :
print(aw.Node.node_type_to_string(childNode.node_type))
# Recurse into the node if it is a composite node.
if childNode.is_composite :
self.traverse_all_nodes(childNode.as_composite_node())

对子节点和父节点 {#typed-access-to-child-and-parent-nodes} 的类型化访问

到目前为止,我们已经讨论了返回基本类型之一的属性 - NodeCompositeNode。但有时,您可能需要将值转换为特定的节点类,例如 RunParagraph。也就是说,在使用复合的 Aspose.Words DOM 时,您无法完全摆脱强制转换。

为了减少转换的需要,大多数 Aspose.Words 类提供了提供强类型访问的属性和集合。类型化访问有三种基本模式:

类型化属性只是有用的快捷方式,有时比从 Node.parent_nodeCompositeNode.first_child 继承的通用属性提供更容易的访问。

以下代码示例演示如何使用类型化属性来访问文档树的节点:

# For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Python-via-.NET
doc = aw.Document()
section = doc.first_section
body = section.body
# Quick typed access to all Table child nodes contained in the Body.
tables = body.tables
for table in tables :
# Quick typed access to the first row of the table.
if table.first_row != None :
table.first_row.remove()
# Quick typed access to the last row of the table.
if table.last_row != None :
table.last_row.remove()