Bekerja dengan Pohon Node Dokumen

Bekerja dengan Pohon Node Dokumen

Bekerja dengan Pohon Node Dokumen

Panduan ini menunjukkan cara menavigasi dan memanipulasi struktur dokumen secara langsung melalui pohon node-nya — model objek yang Document, Section, Paragraph, Run, Table, dan setiap elemen dokumen lainnya dibangun di atasnya. Setiap elemen dalam dokumen diturunkan dari Node, dan setiap elemen yang dapat menampung anak diturunkan dari CompositeNode, sehingga memahami dua kelas dasar ini menjelaskan cara bekerja dengan konten yang belum ditangani oleh API khusus.


Kelas Dasar Node dan CompositeNode

Node adalah kelas dasar abstrak untuk setiap objek dalam pohon dokumen. Ia mengekspos NodeType (nilai enum yang mengidentifikasi jenis node ini), ParentNode (CompositeNode yang membungkus), Document (dokumen pemilik), dan PreviousSibling/NextSibling untuk berpindah antar node pada tingkat yang sama. Node.IsComposite melaporkan apakah sebuah node dapat menampung anak. CompositeNode memperluas Node untuk elemen kontainer — FirstChild, LastChild, dan Count menggambarkan anak langsungnya, dan HasChildNodes melaporkan apakah ada anak. CompositeNode.GetChildNodes(nodeType, isDeep) mengambil semua anak dari NodeType tertentu, secara opsional mencari seluruh subtree (isDeep) bukan hanya anak langsung, dan CompositeNode.GetChild(nodeType, index, isDeep) mengambil satu anak berdasarkan indeks.

Enum NodeType

NodeType mendaftar setiap jenis node yang didefinisikan oleh model dokumen — tipe struktural seperti Document, Section, Body, Paragraph, Table, Row, dan Cell; tipe konten inline seperti Run, Shape, dan Comment; serta tipe mesin field seperti FieldStart, FieldSeparator, dan FieldEnd, yang menandai batas dan teks saklar dari sebuah field Word dalam aliran run. Mengirimkan nilai NodeType ke GetChildNodes atau GetChild membatasi pencarian ke node dengan tipe tersebut secara tepat, atau kirim NodeType.Any untuk mencocokkan semua tipe node.

Menelusuri dan Memodifikasi Pohon

CompositeNode.GetEnumerator() memungkinkan Anda mengulangi anak langsung suatu node, dan CompositeNode juga mengimplementasikan SelectNodes(xpath) serta SelectSingleNode(xpath) untuk kueri berbasis XPath di seluruh subtree. Untuk memodifikasi pohon, AppendChild(newChild) dan PrependChild(newChild) menambahkan sebuah node di awal atau akhir anak-anak, InsertBefore(newChild, refChild) dan InsertAfter(newChild, refChild) menyisipkan relatif terhadap child yang ada, dan RemoveChild(oldChild) atau RemoveAllChildren() menghapus node. CompositeNode.IndexOf(child) mengembalikan posisi child. Pada Node sendiri, Clone(isCloneChildren) membuat salinan — opsional mendalam — dan Remove() melepaskan node dari induknya. Node.NextPreOrder(rootNode) dan PreviousPreOrder(rootNode) melangkah melalui pohon dalam urutan dokumen, dibatasi oleh node root tertentu, yang merupakan cara umum untuk memindai seluruh subtree node-per-node tanpa kode rekursif.

Koleksi Node dan Notifikasi Perubahan

NodeCollection adalah koleksi node konkret yang dapat diubah yang mendukung Add(node), Insert(index, node), Remove(node), RemoveAt(index), Clear(), Contains(node), IndexOf(node), dan ToArray(), serta properti Count. NodeList dan NodeEnumerator mendukung iterasi gaya-baca atas hasil kueri, seperti node yang dikembalikan oleh SelectNodes. Untuk mengamati perubahan struktural secara real-time, implementasikan INodeChangingCallback — metode NodeInserting(args), NodeInserted(args), NodeRemoving(args), dan NodeRemoved(args) masing-masing menerima sebuah NodeChangingArgs yang menjelaskan Node yang terpengaruh, OldParent/NewParent-nya, dan NodeChangingAction yang terjadi.

Mengunjungi Node dengan DocumentVisitor

Untuk memproses setiap tipe node secara terstruktur tanpa harus menelusuri pohon secara manual, buat subclass DocumentVisitor dan timpa metode Visit*Start/Visit*End untuk tipe node yang Anda butuhkan — VisitParagraphStart/VisitParagraphEnd, VisitTableStart/VisitTableEnd, VisitRun, dan seterusnya — kemudian panggil Node.Accept(visitor) pada root subtree untuk menjalankannya. Ini adalah pola standar untuk mengekspor, mentransformasi, atau memeriksa sebuah dokumen tanpa menulis traversal rekursif khusus untuk setiap kasus penggunaan.


Tips dan Praktik Terbaik

  • Gunakan NodeType.Any dengan GetChildNodes(NodeType.Any, true) ketika Anda membutuhkan setiap keturunan tanpa mempedulikan tipe, daripada memanggil GetChildNodes sekali per tipe.
  • Lebih pilih Node.NextPreOrder(rootNode) / PreviousPreOrder(rootNode) untuk pemindaian datar, non-rekursif pada subtree daripada menulis penurunan rekursif manual.
  • Implementasikan DocumentVisitor daripada kode penelusuran pohon ad hoc ketika pemrosesan yang sama perlu dijalankan pada banyak tipe node yang berbeda.
  • Periksa Node.IsComposite sebelum melakukan casting ke CompositeNode saat bekerja dengan node dengan tipe yang tidak diketahui.
  • Daftarkan sebuah INodeChangingCallback ketika Anda perlu merespons penyisipan atau penghapusan yang dilakukan oleh kode lain (termasuk operasi bawaan), bukan hanya perubahan yang dibuat secara langsung oleh kode Anda sendiri.

Masalah Umum

MasalahPenyebabPerbaikan
Mencasting Node ke CompositeNode menghasilkan error invalid-castNode tersebut adalah tipe daun (misalnya Run atau FieldStart) yang tidak dapat memiliki anakPeriksa Node.IsComposite terlebih dahulu, atau gunakan NodeType untuk memastikan node adalah tipe kontainer sebelum mencasting
GetChildNodes hanya mengembalikan anak langsungisDeep dibiarkan false (atau dihilangkan di mana overload defaultnya adalah shallow)Berikan isDeep: true untuk mencari seluruh subtree
Memodifikasi koleksi saat mengiterasinya menimbulkan kesalahanNode ditambahkan atau dihapus di dalam foreach atas NodeCollection atau hasil dari GetChildNodesMaterialisasikan node yang akan dimodifikasi dengan ToArray() terlebih dahulu, lalu iterasi array sambil memutasi pohon
Subkelas DocumentVisitor tidak dipanggil untuk beberapa kontenVisitor dipanggil melalui Accept() pada node yang tidak berisi konten target, atau metode Visit* yang bersangkutan tidak dioverridePanggil Accept() pada nenek moyang yang berisi konten, dan override setiap pasangan Visit*Start/Visit*End yang relevan dengan tipe node yang diproses

FAQ

Apa perbedaan antara Node dan CompositeNode?

Node adalah kelas dasar yang menjadi induk bagi semua elemen dokumen. CompositeNode memperluas Node untuk elemen yang dapat memiliki anak (seperti Body, Paragraph, dan Table); elemen daun (seperti Run) mewarisi langsung dari Node dan tidak dapat menampung anak.

Bagaimana cara saya menemukan semua node dengan tipe tertentu dalam sebuah dokumen?

Panggil CompositeNode.GetChildNodes(nodeType, isDeep) pada node yang menjadi akar pencarian Anda (seringkali Document itu sendiri), dengan memberikan NodeType yang Anda inginkan dan isDeep: true untuk mencari seluruh subtree, bukan hanya anak langsung.

Bagaimana cara menghapus node dengan aman saat iterasi?

Kumpulkan node yang akan dihapus ke dalam sebuah array terlebih dahulu — misalnya dengan NodeCollection.ToArray() — kemudian iterasi array tersebut dan panggil Remove() pada setiap node, alih-alih memodifikasi koleksi langsung saat foreach sedang mengiterasinya.

Apa itu node FieldStart, FieldSeparator, dan FieldEnd?

Mereka adalah nilai NodeType yang menandai mekanisme bidang Word yang disematkan dalam aliran run: FieldStart dan FieldEnd mengikat bidang tersebut, dan FieldSeparator memisahkan kode bidang dari hasil yang saat ini ditampilkan.

Bagaimana cara memproses setiap paragraf, tabel, dan run dalam sebuah dokumen?

Buat subclass DocumentVisitor, timpa VisitParagraphStart/VisitParagraphEnd, VisitTableStart/VisitTableEnd, dan VisitRun untuk tipe node yang Anda butuhkan, lalu panggil Accept(visitor) pada Document (atau akar subtree mana pun) untuk menjalankannya.


API Reference Ringkasan

Kelas/MetodeDeskripsi
NodeKelas dasar abstrak untuk setiap node dalam pohon dokumen
CompositeNodeKelas dasar untuk node yang dapat berisi anak
CompositeNode.GetChildNodes(nodeType, isDeep)Ambil semua anak dari tipe tertentu, secara opsional di seluruh subtree
CompositeNode.AppendChild(newChild) / InsertBefore(newChild, refChild)Tambahkan node ke container di akhir, atau sebelum/setelah child yang ada
NodeTypeEnum yang mengidentifikasi jenis node (Paragraph, Table, Run, FieldStart, dan seterusnya)
NodeCollectionKoleksi konkret yang dapat diubah dari objek Node
Node.Clone(isCloneChildren)Salin node, secara opsional termasuk anak-anaknya
Node.NextPreOrder(rootNode) / PreviousPreOrder(rootNode)Lalui subtree dalam urutan dokumen
INodeChangingCallbackAntarmuka callback yang diberi notifikasi saat node disisipkan atau dihapus
DocumentVisitorKelas dasar untuk penelusuran terstruktur, type-dispatched via Node.Accept(visitor)

Lihat Juga

 Bahasa Indonesia