Aspose.Words об'єктна модель документа (DOM)
Об’єктна модель документа Aspose.Words (DOM) являє собою представлення документа Word в пам’яті. Об’єктна модель документа Aspose.Words DOM дозволяє програмно зчитувати вміст і форматування документа Word, маніпулювати ними і змінювати їх.
У цьому розділі описуються основні класи Aspose.Words DOM та їх взаємозв’язки. Використовуючи класи Aspose.Words DOM, ви можете отримати програмний доступ до елементів документа та форматування.
Створити дерево об’єктів документа
Коли документ зчитується в Aspose.Words DOM, то будується дерево об’єктів, і різні типи елементів вихідного документа мають свої власні об’єкти дерева DOM з різними властивостями.
Побудова дерева вузлів документа
Коли Aspose.Words зчитує документ Word в пам’ять, він створює об’єкти різних типів, які представляють різні елементи документа. Кожен фрагмент тексту, абзац, таблиця або розділ є вузлом, і навіть сам документ є вузлом. Aspose.Words визначає клас для кожного типу вузла документа.
Дерево документа в Aspose.Words відповідає шаблону складеного проектування:
- Всі класи вузлів в кінцевому рахунку є похідними від класу Node, який є базовим класом в об’єктній моделі документа Aspose.Words.
- Вузли, які можуть містити інші вузли, наприклад, Section або Paragraph, є похідними від класу CompositeNode, який, у свою чергу, є похідним від класу Node.
На наведеній нижче діаграмі показано успадкування між вузловими класами об’єктної моделі документа Aspose.Words (DOM). Назви абстрактних класів виділені курсивом.

Node
.
Давайте розглянемо приклад. На наступному малюнку показано документ Microsoft Word з різними типами вмісту.

При читанні наведеного вище документа в Aspose.Words DOM створюється дерево об’єктів, як показано на схемі нижче.

Document, Section, Paragraph, Table, Shape, Run, а всі інші еліпси на діаграмі є об’єктами Aspose.Words, що представляють елементи документа Word.
Отримуємо Node
Тип
Хоча класу Node достатньо, щоб відрізняти різні вузли один від одного, Aspose.Words надає перерахування NodeType для спрощення деяких задач API, таких як вибір вузлів певного типу.
Тип кожного вузла можна визначити за допомогою властивості NodeType. Ця властивість повертає значення перерахування 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-C | |
System::SharedPtr<Document> doc = System::MakeObject<Document>(); | |
// Returns NodeType.Document | |
NodeType type = doc->get_NodeType(); |
Навігація по дереву документів
Aspose.Words представляє документ у вигляді дерева вузлів, яке дозволяє переміщатися між вузлами. У цьому розділі описано, як дослідити дерево документів і переміщатися по ньому в Aspose.Words.
Коли ви відкриваєте зразок документа, представлений раніше, у провіднику документів, дерево вузлів відображається точно так, як воно представлено в Aspose.Words.

Зв’язки вузлів документа
Вузли в дереві мають взаємозв’язки між собою:
- Вузол, що містить інший вузол, є parent.
- Вузол, що міститься в батьківському вузлі, є child. дочірніми вузлами того ж батьківського вузла є sibling вузлів.
- Вузол root завжди є вузлом Document.
Вузли, які можуть містити інші вузли, є похідними від класу CompositeNode, а всі вузли, в кінцевому рахунку, є похідними від класу Node. Ці два базові класи забезпечують загальні методи та властивості для навігації та модифікації деревоподібної структури.
Наступна діаграма об’єктів UML показує кілька вузлів зразка документа та їх зв’язки між собою за допомогою батьківських, дочірніх та споріднених властивостей:

Документ є власником вузла
Вузол завжди належить до певного документа, навіть якщо він був щойно створений або видалений з дерева, оскільки важливі структури всього документа, такі як стилі та списки, зберігаються у вузлі Document. Наприклад, неможливо використовувати Paragraph без Document, оскільки кожному абзацу присвоєно стиль, який визначено глобально для документа. Це правило використовується при створенні будь-яких нових вузлів. Для додавання нового Paragraph безпосередньо до DOM потрібен об’єкт document, переданий конструктору.
При створенні нового абзацу за допомогою DocumentBuilder конструктор завжди має клас Document, пов’язаний з ним через властивість DocumentBuilder.Document.
У наступному прикладі коду показано, що при створенні будь-якого вузла завжди визначається документ, якому буде належати цей вузол:
For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-C | |
// Open a file from disk. | |
System::SharedPtr<Document> doc = System::MakeObject<Document>(); | |
// Creating a new node of any type requires a document passed into the constructor. | |
System::SharedPtr<Paragraph> para = System::MakeObject<Paragraph>(doc); | |
// The new paragraph node does not yet have a parent. | |
std::cout << "Paragraph has no parent node: " << System::ObjectExt::Box<bool>((para->get_ParentNode() == nullptr))->ToString().ToUtf8String() << std::endl; | |
// But the paragraph node knows its document. | |
std::cout << "Both nodes' documents are the same: " << System::ObjectExt::Box<bool>((para->get_Document() == doc))->ToString().ToUtf8String() << std::endl; | |
// 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->get_ParagraphFormat()->set_StyleName(u"Heading 1"); | |
// Now add the paragraph to the main text of the first section. | |
doc->get_FirstSection()->get_Body()->AppendChild(para); | |
// The paragraph node is now a child of the Body node. | |
std::cout << "Paragraph has a parent node: " << System::ObjectExt::Box<bool>((para->get_ParentNode() != nullptr))->ToString().ToUtf8String() << std::endl; |
Батьківський вузол
Кожен вузол має батьківський вузол, вказаний за допомогою властивості ParentNode. Вузол не має батьківського вузла, тобто значення ParentNode дорівнює null у наступних випадках:
- Вузол щойно створений і ще не доданий до дерева.
- Вузол був видалений з дерева.
- Це кореневий вузол Document, який завжди має нульовий батьківський вузол.
Ви можете видалити вузол з батьківського, викликавши метод Remove.Наступний приклад коду показує, як отримати доступ до батьківського вузла:
For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-C | |
// Create a new empty document. It has one section. | |
System::SharedPtr<Document> doc = System::MakeObject<Document>(); | |
// The section is the first child node of the document. | |
System::SharedPtr<Node> section = doc->get_FirstChild(); | |
// The section's parent node is the document. | |
std::cout << "Section parent is the document: " << System::ObjectExt::Box<bool>((doc == section->get_ParentNode()))->ToString().ToUtf8String() << std::endl; |
Дочірні вузли
Найефективніший спосіб отримати доступ до дочірніх вузлів CompositeNode - це використання властивостей FirstChild та LastChild, які повертають перший та останній дочірні вузли відповідно. Якщо немає дочірніх вузлів, ці властивості повертають значення null.
CompositeNode
Якщо вузол не має дочірніх вузлів, то властивість ChildNodes повертає порожню колекцію. Ви можете перевірити, чи містить CompositeNode будь-які дочірні вузли, використовуючи властивість HasChildNodes.
Наступний приклад коду показує, як перерахувати безпосередні дочірні вузли CompositeNode
, використовуючи перелічувач, наданий колекцією ChildNodes
:
For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-C | |
System::SharedPtr<Document> doc = System::MakeObject<Document>(); | |
System::SharedPtr<Paragraph> paragraph = System::DynamicCast<Paragraph>(doc->GetChild(NodeType::Paragraph, 0, true)); | |
System::SharedPtr<NodeCollection> children = paragraph->get_ChildNodes(); | |
for (System::SharedPtr<Node> child : System::IterateOver(children)) | |
{ | |
if (System::ObjectExt::Equals(child->get_NodeType(), NodeType::Run)) | |
{ | |
// Say we found the node that we want, do something useful. | |
System::SharedPtr<Run> run = System::DynamicCast<Run>(child); | |
std::cout << run->get_Text().ToUtf8String() << std::endl; | |
} | |
} |
Наступний приклад коду показує, як перерахувати безпосередні дочірні вузли CompositeNode
за допомогою індексованого доступу:
For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-C | |
System::SharedPtr<Document> doc = System::MakeObject<Document>(); | |
System::SharedPtr<Paragraph> paragraph = System::DynamicCast<Paragraph>(doc->GetChild(NodeType::Paragraph, 0, true)); | |
System::SharedPtr<NodeCollection> children = paragraph->get_ChildNodes(); | |
for (int32_t i = 0; i < children->get_Count(); i++) | |
{ | |
System::SharedPtr<Node> child = children->idx_get(i); | |
// Paragraph may contain children of various types such as runs, shapes and so on. | |
if (System::ObjectExt::Equals(child->get_NodeType(), NodeType::Run)) | |
{ | |
// Say we found the node that we want, do something useful. | |
System::SharedPtr<Run> run = System::DynamicCast<Run>(child); | |
std::cout << run->get_Text().ToUtf8String() << std::endl; | |
} | |
} |
Споріднені вузли
Ви можете отримати вузол, який безпосередньо передує певному вузлу або слідує за ним, використовуючи властивості PreviousSibling та NextSibling відповідно. Якщо вузол є останнім дочірнім по відношенню до свого батьківського вузла, то властивість NextSibling дорівнює null. І навпаки, якщо вузол є першим дочірнім по відношенню до свого батьківського елементу, то властивість PreviousSibling дорівнює null.
Наступний приклад коду показує, як ефективно відвідувати всі прямі та непрямі дочірні вузли складеного вузла:
For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-C | |
void TraverseAllNodes(System::SharedPtr<CompositeNode> parentNode) | |
{ | |
// This is the most efficient way to loop through immediate children of a node. | |
for (System::SharedPtr<Node> childNode = parentNode->get_FirstChild(); childNode != nullptr; childNode = childNode->get_NextSibling()) | |
{ | |
// Do some useful work. | |
std::cout << Node::NodeTypeToString(childNode->get_NodeType()).ToUtf8String() << std::endl; | |
// Recurse into the node if it is a composite node. | |
if (childNode->get_IsComposite()) | |
{ | |
TraverseAllNodes(System::DynamicCast<CompositeNode>(childNode)); | |
} | |
} | |
} | |
void RecurseAllNodes(System::String const &inputDataDir) | |
{ | |
// Open a document. | |
System::SharedPtr<Document> doc = System::MakeObject<Document>(inputDataDir + u"Node.RecurseAllNodes.doc"); | |
// Invoke the recursive function that will walk the tree. | |
TraverseAllNodes(doc); | |
} |
Типізований доступ до дочірніх і батьківських вузлів
До цього часу ми обговорювали властивості, які повертають один із базових типів – Node або CompositeNode. Але іноді виникають ситуації, коли може знадобитися привести значення до певного класу вузлів, наприклад Run або Paragraph. Тобто, ви не можете повністю відмовитися від приведення при роботі з Aspose.Words DOM, який є складовим.
Щоб зменшити потребу в приведенні у відповідність, більшість класів Aspose.Words надають властивості та колекції, які забезпечують суворо набраний доступ. Існує три основні шаблони типізованого доступу:
- Батьківський вузол надає набрані властивості FirstXXX та LastXXX. Наприклад, у Document є властивості FirstSection і LastSection. Аналогічно, у Table є такі властивості, як FirstRow, LastRow та інші.
- Батьківський вузол надає набрану колекцію дочірніх вузлів, таких як Document.Sections, Body.Paragraphs та інші.
- Дочірній вузол надає набраний доступ до свого батьківського вузла, наприклад Run.ParentParagraph, Paragraph.ParentSection та інші.
Набрані властивості-це просто корисні ярлики, які іноді забезпечують легший доступ, ніж загальні властивості, успадковані від Node.ParentNode та CompositeNode.FirstChild.
Наступний приклад коду показує, як використовувати набрані властивості для доступу до вузлів дерева документів:
For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-C | |
System::SharedPtr<Document> doc = System::MakeObject<Document>(); | |
System::SharedPtr<Section> section = doc->get_FirstSection(); | |
// Quick typed access to the Body child node of the Section. | |
System::SharedPtr<Body> body = section->get_Body(); | |
// Quick typed access to all Table child nodes contained in the Body. | |
System::SharedPtr<TableCollection> tables = body->get_Tables(); | |
for (System::SharedPtr<Table> table : System::IterateOver<System::SharedPtr<Table>>(tables)) | |
{ | |
// Quick typed access to the first row of the table. | |
if (table->get_FirstRow() != nullptr) | |
{ | |
table->get_FirstRow()->Remove(); | |
} | |
// Quick typed access to the last row of the table. | |
if (table->get_LastRow() != nullptr) | |
{ | |
table->get_LastRow()->Remove(); | |
} | |
} |