Aspose.Words Modello a oggetti documento (DOM)

Il Aspose.Words Document Object Model (DOM) è una rappresentazione in memoria di un documento Word. Il DOM Aspose.Words consente di leggere, manipolare e modificare a livello di programmazione il contenuto e la formattazione di un documento Word.

Questa sezione descrive le classi principali del DOM Aspose.Words e le loro relazioni. Utilizzando le classi DOM Aspose.Words, è possibile ottenere l’accesso programmatico agli elementi del documento e alla formattazione.

Crea Albero oggetti documento

Quando un documento viene letto nel DOM Aspose.Words, viene creato un albero di oggetti e diversi tipi di elementi del documento di origine hanno i propri oggetti albero DOM con varie proprietà.

Struttura dei nodi del documento

Quando Aspose.Words legge un documento Word in memoria, crea oggetti di diversi tipi che rappresentano vari elementi del documento. Ogni esecuzione di un testo, paragrafo, tabella o sezione è un nodo e anche il documento stesso è un nodo. Aspose.Words definisce una classe per ogni tipo di nodo documento.

L’albero del documento in Aspose.Words segue il modello di progettazione composito:

  • Tutte le classi di nodi derivano in ultima analisi dalla classe Node, che è la classe base nel modello a oggetti del documento Aspose.Words.
  • I nodi che possono contenere altri nodi, ad esempio Section o Paragraph, derivano dalla classe CompositeNode, che a sua volta deriva dalla classe Node.

Il diagramma riportato di seguito mostra l’ereditarietà tra le classi di nodi del modello a oggetti Document (DOM) Aspose.Words. I nomi delle classi astratte sono in corsivo.

aspose-words-dom-aspose-words-cpp

Diamo un’occhiata a un esempio. L’immagine seguente mostra un documento Microsoft Word con diversi tipi di contenuto.

document-example-aspose-words-cpp

Quando si legge il documento sopra nel DOM Aspose.Words, viene creato l’albero degli oggetti, come mostrato nello schema seguente.

document-example-dom-aspose-words-cpp

Document, Section, Paragraph, Table, Shape, Run, e tutte le altre ellissi sul diagramma sono oggetti Aspose.Words che rappresentano elementi del documento Word.

Ottieni un Node Tipo

Sebbene la classe Node sia sufficiente per distinguere nodi diversi l’uno dall’altro, Aspose.Words fornisce l’enumerazione NodeType per semplificare alcune attività API, come la selezione di nodi di un tipo specifico.

Il tipo di ciascun nodo può essere ottenuto utilizzando la proprietà NodeType. Questa proprietà restituisce un NodeType enumeration value. For example, a paragraph node represented by the Paragraph class returns NodeType.Paragraph e un nodo tabella rappresentato dal Table class returns NodeType.Table.

L’esempio seguente mostra come ottenere un tipo di nodo utilizzando l’enumerazione 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();

Navigazione Albero documenti

Aspose.Words rappresenta un documento come albero dei nodi, che consente di spostarsi tra i nodi. Questa sezione descrive come esplorare e navigare nell’albero dei documenti in Aspose.Words.

Quando si apre il documento di esempio, presentato in precedenza, in Esplora documenti, l’albero dei nodi appare esattamente come è rappresentato in Aspose.Words.

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

Relazioni nodo documento

I nodi nell’albero hanno relazioni tra di loro:

  • Un nodo contenente un altro nodo è parent.
  • Il nodo contenuto nel nodo padre è un child. Nodi figlio dello stesso genitore sono sibling nodi.
  • Il nodo root è sempre il nodo Document.

I nodi che possono contenere altri nodi derivano dalla classe CompositeNode e tutti i nodi alla fine derivano dalla classe Node. Queste due classi base forniscono metodi e proprietà comuni per la navigazione e la modifica della struttura ad albero.

Il seguente diagramma dell’oggetto UML mostra diversi nodi del documento di esempio e le loro relazioni tra loro tramite le proprietà padre, figlio e fratello:

document-nodes-relationships-aspose-words-cpp

Il documento è proprietario del nodo

Un nodo appartiene sempre a un particolare documento, anche se è stato appena creato o rimosso dall’albero, perché strutture vitali a livello di documento come stili ed elenchi sono memorizzate nel nodo Document. Ad esempio, non è possibile avere un Paragraph senza un Document perché ogni paragrafo ha uno stile assegnato definito globalmente per il documento. Questa regola viene utilizzata quando si creano nuovi nodi. L’aggiunta di un nuovo Paragraph direttamente al DOM richiede un oggetto documento passato al costruttore.

Quando si crea un nuovo paragrafo usando DocumentBuilder, il builder ha sempre una classe Document collegata ad esso tramite la proprietà DocumentBuilder.Document.

L’esempio di codice seguente mostra che quando si crea un nodo, viene sempre definito un documento che possederà il nodo:

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;

Nodo padre

Ogni nodo ha un genitore specificato dalla proprietà ParentNode. Un nodo non ha un nodo padre, cioè ParentNode è null, nei seguenti casi:

  • Il nodo è stato appena creato e non è ancora stato aggiunto all’albero.
  • Il nodo è stato rimosso dall’albero.
  • Questo è il nodo root Document che ha sempre un nodo padre nullo.

È possibile rimuovere un nodo dal suo genitore chiamando il metodo Remove.Il seguente esempio di codice mostra come accedere al nodo padre:

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;

Nodi figlio

Il modo più efficiente per accedere ai nodi figlio di un CompositeNode è tramite le proprietà FirstChild e LastChild che restituiscono rispettivamente il primo e l’ultimo nodo figlio. Se non ci sono nodi figlio, queste proprietà restituiscono null.

CompositeNode

Se un nodo non ha figli, la proprietà ChildNodes restituisce una raccolta vuota. È possibile verificare se CompositeNode contiene nodi figlio utilizzando la proprietà HasChildNodes.

L’esempio di codice seguente mostra come enumerare i nodi figlio immediati di un CompositeNode utilizzando l’enumeratore fornito dalla raccolta 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;
}
}

L’esempio di codice seguente mostra come enumerare i nodi figlio immediati di un CompositeNode utilizzando l’accesso indicizzato:

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

Nodi fratelli

È possibile ottenere il nodo che precede o segue immediatamente un particolare nodo utilizzando rispettivamente le proprietà PreviousSibling e NextSibling. Se un nodo è l’ultimo figlio del suo genitore, allora la proprietà NextSibling è null. Al contrario, se il nodo è il primo figlio del suo genitore, la proprietà PreviousSibling è null.

Il seguente esempio di codice mostra come visitare in modo efficiente tutti i nodi figlio diretti e indiretti di un nodo composito:

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

Accesso digitato ai nodi figlio e padre

Finora, abbiamo discusso le proprietà che restituiscono uno dei tipi di base - Node o CompositeNode. Ma a volte ci sono situazioni in cui potrebbe essere necessario trasmettere valori a una specifica classe di nodi, ad esempio Run o Paragraph. Cioè, non è possibile allontanarsi completamente dal casting quando si lavora con il DOM Aspose.Words, che è composito.

Per ridurre la necessità di eseguire il casting, la maggior parte delle classi Aspose.Words fornisce proprietà e raccolte che forniscono un accesso fortemente tipizzato. Ci sono tre modelli di base di accesso digitato:

Le proprietà digitate sono semplicemente scorciatoie utili che a volte forniscono un accesso più semplice rispetto alle proprietà generiche ereditate da Node.ParentNode e CompositeNode.FirstChild.

L’esempio di codice seguente mostra come utilizzare le proprietà tipizzate per accedere ai nodi della struttura del documento:

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