Lavorare con l'albero dei nodi del documento

Lavorare con l'albero dei nodi del documento

Lavorare con l’albero dei nodi del documento

Questa guida mostra come navigare e manipolare la struttura di un documento direttamente attraverso il suo albero dei nodi — il modello a oggetti su cui sono costruiti Document, Section, Paragraph, Run, Table e ogni altro elemento del documento. Ogni elemento in un documento deriva da Node, e ogni elemento capace di contenere figli deriva da CompositeNode, quindi comprendere queste due classi base spiega come lavorare con contenuti che nessun helper dedicato API copre già.


Le classi base Node e CompositeNode

Node è la classe base astratta per ogni oggetto in un albero di documento. Espone NodeType (un valore enum che identifica il tipo di nodo), ParentNode (il CompositeNode contenitore), Document (il documento proprietario), e PreviousSibling/NextSibling per spostarsi tra nodi allo stesso livello. Node.IsComposite indica se un dato nodo può contenere figli. CompositeNode estende Node per gli elementi contenitori — FirstChild, LastChild e Count descrivono i suoi figli immediati, e HasChildNodes segnala se ne esistono. CompositeNode.GetChildNodes(nodeType, isDeep) recupera tutti i figli di un dato NodeType, opzionalmente cercando l’intero sottoalbero (isDeep) invece solo i figli diretti, e CompositeNode.GetChild(nodeType, index, isDeep) recupera uno singolo per indice.

L’Enum NodeType

NodeType elenca ogni tipo di nodo definito dal modello di documento — tipi strutturali come Document, Section, Body, Paragraph, Table, Row e Cell; tipi di contenuto inline come Run, Shape e Comment; e tipi di meccanismo di campo come FieldStart, FieldSeparator e FieldEnd, che contraddistinguono i confini e il testo di commutazione di un campo Word all’interno del flusso di run. Passare un valore NodeType a GetChildNodes o GetChild limita la ricerca ai nodi di esattamente quel tipo, oppure passare NodeType.Any per corrispondere a tutti i tipi di nodo.

Attraversare e modificare l’albero

CompositeNode.GetEnumerator() ti consente di iterare sui figli diretti di un nodo, e CompositeNode implementa anche SelectNodes(xpath) e SelectSingleNode(xpath) per query basate su XPath sull’intero sottoalbero. Per modificare l’albero, AppendChild(newChild) e PrependChild(newChild) aggiungono un nodo all’inizio o alla fine dei figli, InsertBefore(newChild, refChild) e InsertAfter(newChild, refChild) inseriscono rispetto a un figlio esistente, e RemoveChild(oldChild) o RemoveAllChildren() rimuovono i nodi. CompositeNode.IndexOf(child) restituisce la posizione di un figlio. Su Node stesso, Clone(isCloneChildren) crea una copia — opzionalmente profonda — e Remove() stacca il nodo dal suo genitore. Node.NextPreOrder(rootNode) e PreviousPreOrder(rootNode) percorrono l’albero in ordine documentale, limitati a un nodo radice specificato, il che è un modo comune per scandire un intero sottoalbero nodo per nodo senza codice ricorsivo.

Collezioni di Nodi e Notifiche di Modifica

NodeCollection è una collezione concreta e mutabile di nodi che supporta Add(node), Insert(index, node), Remove(node), RemoveAt(index), Clear(), Contains(node), IndexOf(node) e ToArray(), insieme a una proprietà Count. NodeList e NodeEnumerator supportano l’iterazione in stile lettura sui risultati di una query, come i nodi restituiti da SelectNodes. Per osservare le modifiche strutturali man mano che avvengono, implementa INodeChangingCallback — i suoi metodi NodeInserting(args), NodeInserted(args), NodeRemoving(args) e NodeRemoved(args) ricevono ciascuno un NodeChangingArgs che descrive il Node interessato, il suo OldParent/NewParent, e il NodeChangingAction che si è verificato.

Visita dei Nodi con DocumentVisitor

Per elaborare ogni tipo di nodo in modo strutturato senza percorrere manualmente l’albero, crea una sottoclasse di DocumentVisitor e sovrascrivi i metodi Visit*Start/Visit*End per i tipi di nodo di tuo interesse — VisitParagraphStart/VisitParagraphEnd, VisitTableStart/VisitTableEnd, VisitRun, e così via — quindi chiama Node.Accept(visitor) sulla radice del sottoalbero per eseguirlo. Questo è il modello standard per esportare, trasformare o ispezionare un documento senza scrivere un’attraversata ricorsiva personalizzata per ogni caso d’uso.


Suggerimenti e Buone Pratiche

  • Usa NodeType.Any con GetChildNodes(NodeType.Any, true) quando ti servono tutti i discendenti indipendentemente dal tipo, anziché chiamare GetChildNodes una volta per tipo.
  • Preferisci Node.NextPreOrder(rootNode) / PreviousPreOrder(rootNode) per una scansione piatta e non ricorsiva di un sottoalbero invece di scrivere una discesa ricorsiva manuale.
  • Implementa DocumentVisitor anziché codice ad hoc di attraversamento dell’albero quando la stessa elaborazione deve essere eseguita su molti tipi di nodo diversi.
  • Verifica Node.IsComposite prima di effettuare il cast a CompositeNode quando lavori con un nodo di tipo sconosciuto.
  • Registra un INodeChangingCallback quando devi reagire a inserimenti o rimozioni effettuati da altro codice (incluse le operazioni integrate), non solo alle modifiche che il tuo codice apporta direttamente.

Problemi comuni

ProblemaCausaCorrezione
Il cast di un Node a CompositeNode genera un errore di cast non validoIl nodo è di tipo foglia (ad esempio Run o FieldStart) che non può contenere figliControlla prima Node.IsComposite, oppure usa NodeType per confermare che il nodo sia di tipo contenitore prima di eseguire il cast
GetChildNodes restituisce solo i figli direttiisDeep è stato lasciato false (o omesso dove il sovraccarico predefinisce shallow)Passa isDeep: true per cercare l’intero sottoalbero
Modificare una collezione mentre la si itera genera un erroreI nodi sono stati aggiunti o rimossi all’interno di un foreach su NodeCollection o dal risultato di GetChildNodesMaterializza prima i nodi da modificare con ToArray(), poi itera sull’array mentre muta l’albero
Una sottoclasse di DocumentVisitor non viene chiamata per alcuni contenutiIl visitatore è stato invocato tramite Accept() su un nodo che non contiene il contenuto target, oppure il metodo Visit* corrispondente non è stato sovrascrittoChiama Accept() su un antenato che contiene il contenuto e sovrascrivi ogni coppia Visit*Start/Visit*End pertinente ai tipi di nodo in elaborazione

FAQ

Qual è la differenza tra Node e CompositeNode?

Node è la classe base da cui derivano tutti gli elementi del documento. CompositeNode estende Node per gli elementi che possono contenere figli (come Body, Paragraph e Table); gli elementi foglia (come Run) derivano direttamente da Node e non possono contenere figli.

Come posso trovare tutti i nodi di un tipo specifico in un documento?

Chiama CompositeNode.GetChildNodes(nodeType, isDeep) sul nodo che avvia la tua ricerca (spesso il Document stesso), passando il NodeType desiderato e isDeep: true per cercare l’intero sottoalbero, non solo i figli diretti.

Come rimuovere in modo sicuro i nodi durante l’iterazione?

Raccogli prima i nodi da rimuovere in un array — ad esempio con NodeCollection.ToArray() — poi itera su quell’array e chiama Remove() su ogni nodo, invece di modificare la collezione attiva mentre un foreach la sta iterando.

Cosa sono i nodi FieldStart, FieldSeparator e FieldEnd?

Sono valori NodeType che contrassegnano il meccanismo di un campo Word incorporato nel flusso di esecuzione: FieldStart e FieldEnd delimitano il campo, e FieldSeparator separa il codice del campo dal suo risultato attualmente visualizzato.

Come posso elaborare ogni paragrafo, tabella e run in un documento?

Crea una sottoclasse di DocumentVisitor, sovrascrivi VisitParagraphStart/VisitParagraphEnd, VisitTableStart/VisitTableEnd e VisitRun per i tipi di nodo di cui hai bisogno, quindi chiama Accept(visitor) sul Document (o su qualsiasi radice di sottoalbero) per eseguirlo.


API Reference Sommario

Classe/MetodoDescrizione
NodeClasse base astratta per ogni nodo in un albero del documento
CompositeNodeClasse base per i nodi che possono contenere figli
CompositeNode.GetChildNodes(nodeType, isDeep)Recupera tutti i figli di un determinato tipo, opzionalmente nell’intero sottoalbero
CompositeNode.AppendChild(newChild) / InsertBefore(newChild, refChild)Aggiungi un nodo a un contenitore alla fine, o prima/dopo un figlio esistente
NodeTypeEnum che identifica il tipo di un nodo (Paragraph, Table, Run, FieldStart e così via)
NodeCollectionCollezione concreta e mutabile di oggetti Node
Node.Clone(isCloneChildren)Copia un nodo, opzionalmente includendo i suoi figli
Node.NextPreOrder(rootNode) / PreviousPreOrder(rootNode)Scorri un sottoalbero in ordine di documento
INodeChangingCallbackInterfaccia di callback notificata quando i nodi vengono inseriti o rimossi
DocumentVisitorClasse base per l’attraversamento strutturato, dispatch basato sul tipo, tramite Node.Accept(visitor)

Vedi anche

 Italiano