Εργασία με το Document Node Tree

Εργασία με το Document Node Tree

Εργασία με το Δέντρο Κόμβων του Εγγράφου

Αυτός ο οδηγός δείχνει πώς να περιηγηθείτε και να χειριστείτε τη δομή ενός εγγράφου απευθείας μέσω του δέντρου κόμβων — το μοντέλο αντικειμένων στο οποίο Document, Section, Paragraph, Run, Table και κάθε άλλο στοιχείο εγγράφου χτίζονται. Κάθε στοιχείο σε ένα έγγραφο προέρχεται από το Node, και κάθε στοιχείο που μπορεί να περιέχει παιδιά προέρχεται από το CompositeNode, οπότε η κατανόηση αυτών των δύο βασικών κλάσεων εξηγεί πώς να εργάζεστε με περιεχόμενο που δεν καλύπτεται ήδη από κάποιο αφιερωμένο βοηθητικό API.


Οι Βασικές Κλάσεις Node και CompositeNode

Node είναι η αφηρημένη βασική κλάση για κάθε αντικείμενο σε ένα δέντρο εγγράφου. Εκθέτει το NodeType (μια τιμή enum που προσδιορίζει το είδος του κόμβου), το ParentNode (το περιβάλλον CompositeNode), το Document (το ιδιοκτήτη έγγραφο), και τα PreviousSibling/NextSibling για μετακίνηση μεταξύ κόμβων στο ίδιο επίπεδο. Το Node.IsComposite αναφέρει αν ένας συγκεκριμένος κόμβος μπορεί να περιέχει παιδιά. Το CompositeNode επεκτείνει το Node για στοιχεία δοχείων — τα FirstChild, LastChild και Count περιγράφουν τα άμεσα παιδιά του, και το HasChildNodes αναφέρει αν υπάρχουν. Το CompositeNode.GetChildNodes(nodeType, isDeep) ανακτά όλα τα παιδιά ενός δεδομένου NodeType, προαιρετικά αναζητώντας ολόκληρο το υποδέντρο (isDeep) αντί μόνο των άμεσων παιδιών, και το CompositeNode.GetChild(nodeType, index, isDeep) ανακτά ένα μόνο με βάση το δείκτη.

Η NodeType Enum

NodeType απαριθμεί κάθε είδος κόμβου που ορίζει το μοντέλο εγγράφου — δομικούς τύπους όπως Document, Section, Body, Paragraph, Table, Row και Cell; τύπους ενσωματωμένου περιεχομένου όπως Run, Shape και Comment; και τύπους μηχανισμού πεδίων όπως FieldStart, FieldSeparator και FieldEnd, που σημειώνουν τα όρια και το κείμενο εναλλαγής ενός πεδίου Word μέσα στη ροή εκτέλεσης. Η μεταβίβαση μιας τιμής NodeType στο GetChildNodes ή GetChild περιορίζει την αναζήτηση σε κόμβους ακριβώς αυτού του τύπου, ή περάστε NodeType.Any για αντιστοίχιση σε κάθε τύπο κόμβου.

Περιήγηση και Τροποποίηση του Δέντρου

CompositeNode.GetEnumerator() σας επιτρέπει να επαναλαμβάνετε τα άμεσα παιδιά ενός κόμβου, και το CompositeNode υλοποιεί επίσης το SelectNodes(xpath) και το SelectSingleNode(xpath) για ερωτήματα βάσει XPath σε όλο το υποδέντρο. Για να τροποποιήσετε το δέντρο, τα AppendChild(newChild) και PrependChild(newChild) προσθέτουν έναν κόμβο στην αρχή ή στο τέλος των παιδιών, τα InsertBefore(newChild, refChild) και InsertAfter(newChild, refChild) εισάγουν σχετικά με έναν υπάρχοντα παιδί, και τα RemoveChild(oldChild) ή RemoveAllChildren() αφαιρούν κόμβους. Το CompositeNode.IndexOf(child) επιστρέφει τη θέση ενός παιδιού. Στο ίδιο το Node, το Clone(isCloneChildren) δημιουργεί ένα αντίγραφο — προαιρετικά βαθύ — και το Remove() αποσυνδέει τον κόμβο από τον γονέα του. Τα Node.NextPreOrder(rootNode) και PreviousPreOrder(rootNode) περνούν μέσα από το δέντρο με τη σειρά του εγγράφου, περιορισμένα από έναν δοσμένο ριζικό κόμβο, που είναι ένας κοινός τρόπος για σάρωση ολόκληρου υποδέντρου κόμβο-κατά-κόμβο χωρίς επαναληπτικό κώδικα.

Συλλογές Κόμβων και Ειδοποιήσεις Αλλαγών

NodeCollection είναι μια συγκεκριμένη, μεταβλητή συλλογή κόμβων που υποστηρίζει τα Add(node), Insert(index, node), Remove(node), RemoveAt(index), Clear(), Contains(node), IndexOf(node) και ToArray(), μαζί με μια ιδιότητα Count. Τα NodeList και NodeEnumerator υποστηρίζουν επανάληψη τύπου ανάγνωσης πάνω σε αποτελέσματα ερωτημάτων, όπως οι κόμβοι που επιστρέφει το SelectNodes. Για την παρακολούθηση δομικών αλλαγών καθώς συμβαίνουν, υλοποιήστε το INodeChangingCallback — οι μέθοδοι του NodeInserting(args), NodeInserted(args), NodeRemoving(args) και NodeRemoved(args) λαμβάνουν καθεμία ένα NodeChangingArgs που περιγράφει το επηρεασμένο Node, το OldParent/NewParent του, και το NodeChangingAction που συνέβη.

Επίσκεψη Κόμβων με DocumentVisitor

Για την επεξεργασία κάθε τύπου κόμβου με δομημένο τρόπο χωρίς χειροκίνητη διέλευση του δέντρου, κληρονομήστε το DocumentVisitor και υπερκαλύψτε τις μεθόδους Visit*Start/Visit*End για τους τύπους κόμβων που σας ενδιαφέρουν — VisitParagraphStart/VisitParagraphEnd, VisitTableStart/VisitTableEnd, VisitRun κ.λπ. —, στη συνέχεια καλέστε το Node.Accept(visitor) στη ρίζα του υποδέντρου για να το εκτελέσετε. Αυτό είναι το τυπικό πρότυπο για εξαγωγή, μετασχηματισμό ή επιθεώρηση ενός εγγράφου χωρίς να γράφετε προσαρμοσμένη επαναληπτική περιήγηση για κάθε περίπτωση χρήσης.


Συμβουλές και Καλές Πρακτικές

  • Χρησιμοποιήστε το NodeType.Any με το GetChildNodes(NodeType.Any, true) όταν χρειάζεστε κάθε απόγονο ανεξάρτητα από τον τύπο, αντί να καλέτε το GetChildNodes μία φορά ανά τύπο.
  • Προτιμήστε το Node.NextPreOrder(rootNode) / PreviousPreOrder(rootNode) για μια επίπεδη, μη επαναληπτική σάρωση ενός υποδέντρου αντί να γράφετε χειροκίνητη επαναληπτική κατάβαση.
  • Υλοποιήστε το DocumentVisitor αντί για κώδικα διαδρομής δέντρου ad hoc όταν η ίδια επεξεργασία πρέπει να εκτελείται σε πολλούς διαφορετικούς τύπους κόμβων.
  • Ελέγξτε το Node.IsComposite πριν κάνετε cast στο CompositeNode όταν εργάζεστε με έναν κόμβο άγνωστου τύπου.
  • Καταχωρίστε ένα INodeChangingCallback όταν χρειάζεται να αντιδράσετε σε εισαγωγές ή αφαιρέσεις που γίνονται από άλλο κώδικα (συμπεριλαμβανομένων των ενσωματωμένων λειτουργιών), όχι μόνο στις αλλαγές που κάνει άμεσα ο δικός σας κώδικας.

Συνηθισμένα προβλήματα

ΠρόβλημαΑιτίαΔιόρθωση
Η μετατροπή ενός Node σε CompositeNode προκαλεί σφάλμα μη έγκυρης μετατροπήςΟ κόμβος είναι τύπου φύλλου (π.χ. Run ή FieldStart) που δεν μπορεί να περιέχει παιδιάΕλέγξτε πρώτα το Node.IsComposite, ή χρησιμοποιήστε το NodeType για να επιβεβαιώσετε ότι ο κόμβος είναι τύπου κοντέινερ πριν από τη μετατροπή
GetChildNodes επιστρέφει μόνο άμεσα παιδιάisDeep άφησε false (ή παραλείφθηκε όπου η υπερφόρτωση προεπιλέγεται ως ρηχή)Περνάτε το isDeep: true για να αναζητήσετε ολόκληρο το υποδέντρο
Η τροποποίηση μιας συλλογής ενώ γίνεται επανάληψη πάνω της προκαλεί σφάλμαΠροστέθηκαν ή αφαιρέθηκαν κόμβοι μέσα σε ένα foreach πάνω από NodeCollection ή το αποτέλεσμα του GetChildNodesΔημιουργήστε πρώτα τους κόμβους προς τροποποίηση με το ToArray(), έπειτα επαναλάβετε πάνω στον πίνακα ενώ μεταβάλλετε το δέντρο
Μια υποκατηγορία του DocumentVisitor δεν καλείται για ορισμένο περιεχόμενοΟ επισκέπτης κλήθηκε μέσω Accept() σε έναν κόμβο που δεν περιέχει το στοχευόμενο περιεχόμενο, ή η αντίστοιχη μέθοδος Visit* δεν παρακάμφθηκεΚαλέστε το Accept() σε έναν πρόγονο που περιέχει το περιεχόμενο και παρακάμψτε κάθε ζεύγος Visit*Start/Visit*End σχετικό με τους τύπους κόμβων που επεξεργάζονται

FAQ

Ποια είναι η διαφορά μεταξύ Node και CompositeNode;

Node είναι η βασική κλάση από την οποία κληρονομούν όλα τα στοιχεία του εγγράφου. Το CompositeNode επεκτείνει το Node για στοιχεία που μπορούν να περιέχουν υπο-στοιχεία (όπως τα Body, Paragraph και Table); τα φύλλα (όπως το Run) κληρονομούν απευθείας από το Node και δεν μπορούν να έχουν υπο-στοιχεία.

Πώς μπορώ να βρω όλους τους κόμβους ενός συγκεκριμένου τύπου σε ένα έγγραφο;

Καλέστε το CompositeNode.GetChildNodes(nodeType, isDeep) στον κόμβο που αποτελεί τη ρίζα της αναζήτησής σας (συχνά ο ίδιος ο Document), περνώντας το NodeType που θέλετε και το isDeep: true για να αναζητήσετε ολόκληρο το υποδέντρο, όχι μόνο τα άμεσα παιδιά.

Πώς μπορώ να αφαιρέσω με ασφάλεια κόμβους ενώ κάνω επανάληψη;

Συλλέξτε πρώτα τους κόμβους που θα αφαιρεθούν σε έναν πίνακα — για παράδειγμα με το NodeCollection.ToArray() —, στη συνέχεια επαναλάβετε πάνω σε αυτόν τον πίνακα και καλέστε το Remove() σε κάθε κόμβο, αντί να τροποποιείτε τη ζωντανή συλλογή ενώ ένα foreach την επαναλαμβάνει.

Τι είναι οι κόμβοι FieldStart, FieldSeparator και FieldEnd;

Είναι τιμές NodeType που σηματοδοτούν τη μηχανή ενός πεδίου Word ενσωματωμένου στο ρεύμα εκτέλεσης: τα FieldStart και FieldEnd περιορίζουν το πεδίο, και το FieldSeparator διαχωρίζει τον κώδικα του πεδίου από το τρέχον εμφανιζόμενο αποτέλεσμα.

Πώς μπορώ να επεξεργαστώ κάθε παράγραφο, πίνακα και run σε ένα έγγραφο;

Δημιουργήστε υποκλάση του DocumentVisitor, παρακάμπτετε τα VisitParagraphStart/VisitParagraphEnd, VisitTableStart/VisitTableEnd και VisitRun για τους τύπους κόμβων που χρειάζεστε, στη συνέχεια καλέστε το Accept(visitor) στο Document (ή σε οποιαδήποτε ρίζα υποδέντρου) για να το εκτελέσετε.


API Reference Περίληψη

Κλάση/ΜέθοδοςΠεριγραφή
NodeΑφηρημένη βασική κλάση για κάθε κόμβο σε ένα δέντρο εγγράφου
CompositeNodeΒασική κλάση για κόμβους που μπορούν να περιέχουν παιδιά
CompositeNode.GetChildNodes(nodeType, isDeep)Ανακτήστε όλα τα παιδιά ενός συγκεκριμένου τύπου, προαιρετικά σε ολόκληρο το υποδέντρο
CompositeNode.AppendChild(newChild) / InsertBefore(newChild, refChild)Προσθέστε έναν κόμβο σε ένα δοχείο στο τέλος, ή πριν/μετά από ένα υπάρχον παιδί
NodeTypeΑπαρίθμηση που προσδιορίζει τον τύπο ενός κόμβου (Paragraph, Table, Run, FieldStart, κ.λπ.)
NodeCollectionΣυγκεκριμένη, μεταβλητή συλλογή αντικειμένων Node
Node.Clone(isCloneChildren)Αντιγράψτε έναν κόμβο, προαιρετικά συμπεριλαμβάνοντας τα παιδιά του
Node.NextPreOrder(rootNode) / PreviousPreOrder(rootNode)Περιηγηθείτε σε ένα υποδέντρο με τη σειρά του εγγράφου
INodeChangingCallbackΔιεπαφή callback που ειδοποιείται όταν εισάγονται ή αφαιρούνται κόμβοι
DocumentVisitorΒασική κλάση για δομημένη, με βάση τον τύπο, διάσχιση μέσω Node.Accept(visitor)

Δείτε επίσης

 Ελληνικά