Lucrul cu arborele de noduri al documentului
Lucrul cu arborele de noduri al documentului
Acest ghid arată cum să navigați și să manipulați structura unui document direct prin arborele său de noduri — modelul de obiecte pe care Document, Section, Paragraph, Run, Table și fiecare alt element de document se bazează. Fiecare element dintr-un document derivă din Node, iar fiecare element capabil să conțină copii derivă din CompositeNode, astfel încât înțelegerea acestor două clase de bază explică cum să lucrați cu conținutul pe care niciun ajutor dedicat API nu îl acoperă deja.
Clasele de bază Node și CompositeNode
Node este clasa de bază abstractă pentru fiecare obiect dintr-un arbore de documente. Expune NodeType (o valoare enum care identifică tipul de nod), ParentNode (CompositeNode înconjurător), Document (documentul proprietar) și PreviousSibling/NextSibling pentru navigarea între noduri la același nivel. Node.IsComposite raportează dacă un nod dat poate conține copii. CompositeNode extinde Node pentru elementele container — FirstChild, LastChild și Count descriu copiii săi imediați, iar HasChildNodes raportează dacă există vreunul. CompositeNode.GetChildNodes(nodeType, isDeep) recuperează toți copiii unui NodeType dat, căutând opțional subarborele complet (isDeep) în loc de doar copiii direcți, și CompositeNode.GetChild(nodeType, index, isDeep) recuperează unul singur prin index.
Enumul NodeType
NodeType enumeră fiecare tip de nod pe care modelul de document îl definește — tipuri structurale precum Document, Section, Body, Paragraph, Table, Row și Cell; tipuri de conținut în linie precum Run, Shape și Comment; și tipuri de mecanism de câmp precum FieldStart, FieldSeparator și FieldEnd, care marchează limitele și textul de comutare al unui câmp Word în fluxul de rulare. Transmiterea unei valori NodeType către GetChildNodes sau GetChild restricționează căutarea la noduri de exact acel tip, sau transmiteți NodeType.Any pentru a potrivi fiecare tip de nod.
Parcurgerea și modificarea arborelui
CompositeNode.GetEnumerator() vă permite să iterați peste copiii direcți ai unui nod, iar CompositeNode implementează, de asemenea, SelectNodes(xpath) și SelectSingleNode(xpath) pentru interogări bazate pe XPath în întreg subarborele. Pentru a modifica arborele, AppendChild(newChild) și PrependChild(newChild) adaugă un nod la începutul sau la sfârșitul copiilor, InsertBefore(newChild, refChild) și InsertAfter(newChild, refChild) inserează relativ la un copil existent, și RemoveChild(oldChild) sau RemoveAllChildren() elimină noduri. CompositeNode.IndexOf(child) returnează poziția unui copil. Pe Node însuși, Clone(isCloneChildren) creează o copie — opțional profundă — și Remove() detachează nodul de părintele său. Node.NextPreOrder(rootNode) și PreviousPreOrder(rootNode) parcurg arborele în ordine de document, limitate de un nod rădăcină dat, ceea ce este o metodă comună de a scana întregul subarbore nod cu nod fără cod recursiv.
Colecții de Noduri și Notificări de Modificare
NodeCollection este o colecție concretă, mutabilă de noduri care suportă Add(node), Insert(index, node), Remove(node), RemoveAt(index), Clear(), Contains(node), IndexOf(node) și ToArray(), alături de o proprietate Count. NodeList și NodeEnumerator susțin iterarea în stil de citire peste rezultatele interogărilor, cum ar fi nodurile returnate de SelectNodes. Pentru a observa modificările structurale pe măsură ce apar, implementați INodeChangingCallback — metodele sale NodeInserting(args), NodeInserted(args), NodeRemoving(args) și NodeRemoved(args) primesc fiecare un NodeChangingArgs care descrie Node afectat, OldParent/NewParent sale și NodeChangingAction care a avut loc.
Vizitarea Nodurilor cu DocumentVisitor
Pentru a procesa fiecare tip de nod într-un mod structurat fără a parcurge manual arborele, creați o subclasă a lui DocumentVisitor și suprascrieți metodele Visit*Start/Visit*End pentru tipurile de noduri care vă interesează — VisitParagraphStart/VisitParagraphEnd, VisitTableStart/VisitTableEnd, VisitRun și altele — apoi apelați Node.Accept(visitor) pe rădăcina subarborelui pentru a-l rula. Acesta este modelul standard pentru exportarea, transformarea sau inspecția unui document fără a scrie o traversare recursivă personalizată pentru fiecare caz de utilizare.
Sfaturi și Cele Mai Bune Practici
- Utilizați
NodeType.AnycuGetChildNodes(NodeType.Any, true)când aveți nevoie de fiecare descendent, indiferent de tip, în loc să apelațiGetChildNodeso dată pentru fiecare tip. - Preferați
Node.NextPreOrder(rootNode)/PreviousPreOrder(rootNode)pentru o scanare plată, non-recursivă a unui subarbore în loc să scrieți o coborâre recursivă manuală. - Implementați
DocumentVisitorîn loc de cod ad hoc de parcurgere a arborelui când aceeași procesare trebuie să ruleze pe multe tipuri diferite de noduri. - Verificați
Node.IsCompositeînainte de a face cast laCompositeNodecând lucrați cu un nod de tip necunoscut. - Înregistrați un
INodeChangingCallbackatunci când trebuie să reacționați la inserări sau ștergeri efectuate de alt cod (inclusiv operații încorporate), nu doar la modificările pe care codul dumneavoastră le face direct.
Probleme comune
| Problemă | Cauză | Remediere |
|---|---|---|
Convertirea unui Node în CompositeNode generează o eroare de tip invalid-cast | Nodul este de tip frunză (de exemplu Run sau FieldStart) care nu poate conține copii | Verificați mai întâi Node.IsComposite, sau folosiți NodeType pentru a confirma că nodul este de tip container înainte de conversie |
GetChildNodes returnează doar copiii direcți | isDeep a fost lăsat false (sau omis acolo unde suprasarcina are valoarea implicită shallow) | Pasați isDeep: true pentru a căuta întregul subarbore |
| Modificarea unei colecții în timpul iterării peste ea generează o eroare | Nodurile au fost adăugate sau eliminate în interiorul unui foreach peste NodeCollection sau rezultatul lui GetChildNodes | Materializați nodurile de modificat cu ToArray() mai întâi, apoi iterați peste array în timp ce mutați arborele |
O subclasă DocumentVisitor nu este apelată pentru anumite conținuturi | Vizitatorul a fost invocat prin Accept() pe un nod care nu conține conținutul țintă, sau metoda Visit* corespunzătoare nu a fost suprascrisă | Apelați Accept() pe un strămoș care conține conținutul și suprascrieți fiecare pereche Visit*Start/Visit*End relevantă pentru tipurile de noduri procesate |
FAQ
Care este diferența dintre Node și CompositeNode?
Node este clasa de bază din care derivă fiecare element de document. CompositeNode extinde Node pentru elementele care pot conține copii (cum ar fi Body, Paragraph și Table); elementele frunză (cum ar fi Run) derivă direct din Node și nu pot avea copii.
Cum pot găsi toate nodurile de un anumit tip într-un document?
Apelă CompositeNode.GetChildNodes(nodeType, isDeep) pe nodul care rădăcinează căutarea (de obicei Document însuși), trecând NodeType dorit și isDeep: true pentru a căuta în întreg subarborele, nu doar în copiii direcţi.
Cum pot elimina în siguranță noduri în timpul iterării?
Colectează mai întâi nodurile de eliminat într-un tablou — de exemplu cu NodeCollection.ToArray() — apoi iterează peste acel tablou și apelează Remove() pe fiecare nod, în loc să modifici colecţia activă în timp ce un foreach o iterează.
Ce sunt nodurile FieldStart, FieldSeparator și FieldEnd?
Ele sunt valori NodeType care marchează mecanismul unui câmp Word încorporat în fluxul de rulare: FieldStart și FieldEnd leagă câmpul, iar FieldSeparator separă codul câmpului de rezultatul său afișat în prezent.
Cum pot procesa fiecare paragraf, tabel și rulare dintr-un document?
Creează o subclasă a lui DocumentVisitor, suprascrie VisitParagraphStart/VisitParagraphEnd, VisitTableStart/VisitTableEnd și VisitRun pentru tipurile de noduri de care ai nevoie, apoi apelează Accept(visitor) pe Document (sau pe orice rădăcină de subarbore) pentru a-l executa.
API Reference Rezumat
| Clasă/Metodă | Descriere: |
|---|---|
Node | Clasă de bază abstractă pentru fiecare nod dintr-un arbore de documente |
CompositeNode | Clasă de bază pentru nodurile care pot conține copii |
CompositeNode.GetChildNodes(nodeType, isDeep) | Recuperează toți copiii de un anumit tip, opțional în întregul subarbore |
CompositeNode.AppendChild(newChild) / InsertBefore(newChild, refChild) | Adaugă un nod într-un container la final, sau înainte/după un copil existent |
NodeType | Enumerare care identifică tipul unui nod (Paragraph, Table, Run, FieldStart și așa mai departe) |
NodeCollection | Colecție concretă, mutabilă de obiecte Node |
Node.Clone(isCloneChildren) | Copiază un nod, opțional incluzând copiii săi |
Node.NextPreOrder(rootNode) / PreviousPreOrder(rootNode) | Parcurge un subarbore în ordinea documentului |
INodeChangingCallback | Interfață de callback notificată când nodurile sunt inserate sau eliminate |
DocumentVisitor | Clasă de bază pentru traversare structurată, cu dispecerizare pe tipuri prin Node.Accept(visitor) |