עבודה עם עץ הצמתים של המסמך

עבודה עם עץ הצמתים של המסמך

עבודה עם עץ הצמתים של המסמך

מדריך זה מציג כיצד לנווט ולשנות את מבנה המסמך ישירות דרך עץ הצמתים שלו — מודל האובייקטים שעליו נבנים Document, Section, Paragraph, Run, Table, וכל אלמנט מסמך אחר. כל אלמנט במסמך נגזר מ-Node, וכל אלמנט שיכול להכיל צמתים ילידים נגזר מ-CompositeNode, ולכן הבנת שני המחלקות הבסיסיות הללו מסבירה כיצד לעבוד עם תוכן שאין עבורו עדיין עוזר ייעודי API.


מחלקות הבסיס של הצומת ו-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) מאחזרת אחד מהם לפי אינדקס.

ה-Enum של NodeType

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 במקום קוד הליכה בעץ אד-הוק כאשר יש צורך להריץ את אותו עיבוד על סוגי צמתים שונים רבים.
  • בדוק את Node.IsComposite לפני המרה ל-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)הוסף צומת למכולה בסוף, או לפני/אחרי צומת קיים
NodeTypeEnum המזהה את סוג הצומת (Paragraph, Table, Run, FieldStart, וכן הלאה)
NodeCollectionאוסף קונקרטי, ניתן לשינוי של עצמים מסוג Node
Node.Clone(isCloneChildren)העתק צומת, באופן אופציונלי כולל את צאצאיו
Node.NextPreOrder(rootNode) / PreviousPreOrder(rootNode)התקדם דרך תת-עץ בסדר המסמך
INodeChangingCallbackממשק Callback מודיע כאשר צמתים מוכנסים או מוסרים
DocumentVisitorמחלקת בסיס לניווט מובנה, מפולח לפי סוג באמצעות Node.Accept(visitor)

ראה גם

 עברית