Aspose.Words Obiektowy Model Dokumentu (DOM)

Model obiektowy dokumentu Aspose.Words (DOM) jest reprezentacją dokumentu Word w pamięci. Plik Aspose.Words DOM umożliwia programowe odczytywanie, manipulowanie i modyfikowanie zawartości i formatowania dokumentu Word.

W tej sekcji opisano główne klasy Aspose.Words DOM i ich relacje. Korzystając z klas Aspose.Words DOM, można uzyskać programowy dostęp do elementów dokumentu i formatowania.

Utwórz Drzewo Obiektów Dokumentu

Gdy dokument jest odczytywany do Aspose.Words DOM, budowane jest drzewo obiektów, a różne typy elementów dokumentu źródłowego mają własne DOM obiekty drzewa o różnych właściwościach.

Zbuduj Drzewo Węzłów Dokumentu

Kiedy Aspose.Words odczytuje dokument Word do pamięci, tworzy obiekty różnych typów, które reprezentują różne elementy dokumentu. Każdy przebieg tekstu, akapitu, tabeli lub sekcji jest węzłem, a nawet sam dokument jest węzłem. Aspose.Words definiuje klasę dla każdego typu węzła dokumentu.

Drzewo dokumentów w Aspose.Words jest zgodne z kompozytowym wzorcem projektowym:

  • Wszystkie klasy węzłów ostatecznie wywodzą się z klasy Node, która jest klasą bazową w modelu obiektowym dokumentu Aspose.Words.
  • Węzły, które mogą zawierać inne węzły, na przykład Section lub Paragraph, wywodzą się z klasy CompositeNode, która z kolei wywodzi się z klasy Node.

Poniższy diagram przedstawia dziedziczenie między klasami węzłów modelu obiektowego dokumentu Aspose.Words (DOM). Nazwy klas abstrakcyjnych są pisane kursywą.

aspose-words-dom-aspose-words-cpp

Spójrzmy na przykład. Poniższy obraz przedstawia dokument Microsoft Word z różnymi typami treści.

document-example-aspose-words-cpp

Podczas odczytywania powyższego dokumentu do Aspose.Words DOM tworzone jest drzewo obiektów, jak pokazano na poniższym schemacie.

document-example-dom-aspose-words-cpp

Document, Section, Paragraph, Table, Shape, Run, a wszystkie inne elipsy na diagramie to Aspose.Words obiekty reprezentujące elementy dokumentu Word.

Get a Node Type

Chociaż klasa Node jest wystarczająca do odróżnienia różnych węzłów od siebie, Aspose.Words zapewnia wyliczenie NodeType w celu uproszczenia niektórych zadań API, takich jak wybieranie węzłów określonego typu.

Typ każdego węzła można uzyskać za pomocą właściwości NodeType. Ta właściwość zwraca wartość wyliczenia NodeType. Na przykład węzeł akapitu reprezentowany przez klasę Paragraph zwraca NodeType.Paragraph, a węzeł tabeli reprezentowany przez klasę Table zwraca NodeType.Table.

Poniższy przykład pokazuje, jak uzyskać typ węzła za pomocą wyliczenia 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();

Nawigacja W Drzewie Dokumentów

Aspose.Words reprezentuje dokument jako drzewo węzłów, które umożliwia nawigację między węzłami. W tej sekcji opisano, jak przeglądać i poruszać się po drzewie dokumentów w Aspose.Words.

Po otwarciu przykładowego dokumentu, przedstawionego wcześniej, w Eksploratorze dokumentów, drzewo węzłów pojawia się dokładnie tak, jak jest reprezentowane w Aspose.Words.

document-in-document-explorer-aspose-words-cpp

Relacje Węzła Dokumentu

Węzły w drzewie mają między sobą relacje:

  • Węzeł zawierający inny węzeł to parent.
  • Węzeł zawarty w węźle nadrzędnym to child. węzły podrzędne tego samego rodzica to sibling węzły.
  • Węzeł root jest zawsze węzłem Document.

Węzły, które mogą zawierać inne węzły, pochodzą z klasy CompositeNode, a wszystkie węzły ostatecznie pochodzą z klasy Node. Te dwie klasy bazowe zapewniają wspólne metody i właściwości nawigacji i modyfikacji struktury drzewa.

Poniższy diagram obiektów UML pokazuje kilka węzłów przykładowego dokumentu i ich wzajemne relacje za pośrednictwem właściwości nadrzędnych, podrzędnych i rodzeństwa:

document-nodes-relationships-aspose-words-cpp

Dokument jest właścicielem węzła

Węzeł zawsze należy do określonego dokumentu, nawet jeśli został właśnie utworzony lub usunięty z drzewa, ponieważ ważne struktury całego dokumentu, takie jak style i listy, są przechowywane w węźle Document. Na przykład nie można mieć Paragraph Bez Document, ponieważ każdy akapit ma przypisany styl zdefiniowany globalnie dla dokumentu. Ta reguła jest używana podczas tworzenia nowych węzłów. Dodanie nowego Paragraph bezpośrednio do DOM wymaga obiektu dokumentu przekazanego do konstruktora.

Podczas tworzenia nowego akapitu przy użyciu DocumentBuilder Konstruktor zawsze ma klasę Document połączoną z nim za pośrednictwem właściwości DocumentBuilder.Document.

Poniższy przykład kodu pokazuje, że podczas tworzenia dowolnego węzła dokument, który będzie właścicielem węzła, jest zawsze definiowany:

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;

Węzeł Nadrzędny

Każdy węzeł ma rodzica określonego przez właściwość ParentNode. Węzeł nie ma węzła nadrzędnego, to znaczy ParentNode ma wartość null w następujących przypadkach:

  • Węzeł został właśnie utworzony i nie został jeszcze dodany do drzewa.
  • Węzeł został usunięty z drzewa.
  • Jest to główny węzeł Document, który zawsze ma zerowy węzeł nadrzędny.

Możesz usunąć węzeł z jego rodzica, wywołując metodę Remove.Poniższy przykład kodu pokazuje, jak uzyskać dostęp do węzła nadrzędnego:

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;

Węzły Podrzędne

Najbardziej efektywnym sposobem dostępu do węzłów potomnych CompositeNode jest użycie właściwości FirstChild i LastChild, które zwracają odpowiednio pierwszy i ostatni węzeł potomny. Jeśli nie ma węzłów potomnych, te właściwości zwracają null.

CompositeNode

Jeśli węzeł nie ma potomka, właściwość ChildNodes zwraca pustą kolekcję. Możesz sprawdzić, czy CompositeNode zawiera węzły podrzędne za pomocą właściwości HasChildNodes.

Poniższy przykład kodu pokazuje, jak wyliczyć natychmiastowe węzły potomne CompositeNode za pomocą wylicznika dostarczonego przez zbiór 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;
}
}

Poniższy przykład kodu pokazuje, jak wyliczyć natychmiastowe węzły potomne CompositeNode przy użyciu dostępu indeksowanego:

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;
}
}

Węzły Rodzeństwa

Możesz uzyskać węzeł, który bezpośrednio poprzedza lub podąża za określonym węzłem, używając odpowiednio właściwości PreviousSibling i NextSibling. Jeśli węzeł jest ostatnim dzieckiem swojego rodzica, to właściwość NextSibling to null. I odwrotnie, jeśli węzeł jest pierwszym dzieckiem swojego rodzica, właściwość PreviousSibling to null.

Poniższy przykład kodu pokazuje, jak skutecznie odwiedzać wszystkie bezpośrednie i pośrednie węzły potomne węzła złożonego:

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);
}

Wpisany dostęp do węzłów podrzędnych i nadrzędnych

Do tej pory omówiliśmy właściwości, które zwracają jeden z typów podstawowych – Node lub CompositeNode. Ale czasami zdarzają się sytuacje, w których może być konieczne rzutowanie wartości do określonej klasy węzła, takiej jak Run lub Paragraph. Oznacza to, że nie można całkowicie uciec od rzutowania podczas pracy z Aspose.Words DOM, który jest złożony.

Aby zmniejszyć potrzebę odlewania, większość klas Aspose.Words udostępnia właściwości i kolekcje, które zapewniają dostęp do silnie wpisanego tekstu. Istnieją trzy podstawowe wzorce wpisanego dostępu:

Właściwości wpisywane są jedynie przydatnymi skrótami, które czasami zapewniają łatwiejszy dostęp niż Właściwości ogólne odziedziczone po Node.ParentNode i CompositeNode.FirstChild.

Poniższy przykład kodu pokazuje, jak używać wpisanych właściwości, aby uzyskać dostęp do węzłów drzewa dokumentów:

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();
}
}