Lucrul cu arborele de noduri al documentului

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.Any cu GetChildNodes(NodeType.Any, true) când aveți nevoie de fiecare descendent, indiferent de tip, în loc să apelați GetChildNodes o 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 la CompositeNode când lucrați cu un nod de tip necunoscut.
  • Înregistrați un INodeChangingCallback atunci 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-castNodul este de tip frunză (de exemplu Run sau FieldStart) care nu poate conține copiiVerificaț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țiisDeep 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 eroareNodurile au fost adăugate sau eliminate în interiorul unui foreach peste NodeCollection sau rezultatul lui GetChildNodesMaterializaț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ținuturiVizitatorul 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:
NodeClasă de bază abstractă pentru fiecare nod dintr-un arbore de documente
CompositeNodeClasă 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
NodeTypeEnumerare care identifică tipul unui nod (Paragraph, Table, Run, FieldStart și așa mai departe)
NodeCollectionColecț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
INodeChangingCallbackInterfață de callback notificată când nodurile sunt inserate sau eliminate
DocumentVisitorClasă de bază pentru traversare structurată, cu dispecerizare pe tipuri prin Node.Accept(visitor)

Vezi și:

 Română