Travailler avec l'arbre des nœuds du document
Travailler avec l’arbre de nœuds du document
Ce guide montre comment parcourir et manipuler la structure d’un document directement via son arbre de nœuds — le modèle d’objet sur lequel Document, Section, Paragraph, Run, Table et chaque autre élément de document sont construits. Chaque élément d’un document dérive de Node, et chaque élément capable de contenir des enfants dérive de CompositeNode, de sorte que comprendre ces deux classes de base explique comment travailler avec du contenu qu’aucun assistant dédié API ne couvre déjà.
Les classes de base Node et CompositeNode
Node est la classe de base abstraite pour chaque objet dans un arbre de document. Elle expose NodeType (une valeur d’énumération identifiant le type de nœud), ParentNode (le CompositeNode englobant), Document (le document propriétaire), et PreviousSibling/NextSibling pour se déplacer parmi les nœuds au même niveau. Node.IsComposite indique si un nœud donné peut contenir des enfants. CompositeNode étend Node pour les éléments conteneurs — FirstChild, LastChild et Count décrivent ses enfants immédiats, et HasChildNodes indique s’il en existe. CompositeNode.GetChildNodes(nodeType, isDeep) récupère tous les enfants d’un NodeType donné, en recherchant éventuellement tout le sous-arbre (isDeep) plutôt que seulement les enfants directs, et CompositeNode.GetChild(nodeType, index, isDeep) récupère un seul par index.
L’énumération NodeType
NodeType répertorie chaque type de nœud que le modèle de document définit — les types structurels tels que Document, Section, Body, Paragraph, Table, Row et Cell ; les types de contenu en ligne tels que Run, Shape et Comment ; et les types de mécanisme de champ tels que FieldStart, FieldSeparator et FieldEnd, qui marquent les limites et le texte de commutation d’un champ Word dans le flux d’exécution. Fournir une valeur NodeType à GetChildNodes ou GetChild restreint la recherche aux nœuds de ce type exact, ou fournir NodeType.Any pour correspondre à tous les types de nœuds.
Parcourir et modifier l’arbre
CompositeNode.GetEnumerator() vous permet d’itérer sur les enfants directs d’un nœud, et CompositeNode implémente également SelectNodes(xpath) et SelectSingleNode(xpath) pour les requêtes basées sur XPath à travers le sous-arbre. Pour modifier l’arbre, AppendChild(newChild) et PrependChild(newChild) ajoutent un nœud au début ou à la fin des enfants, InsertBefore(newChild, refChild) et InsertAfter(newChild, refChild) insèrent par rapport à un enfant existant, et RemoveChild(oldChild) ou RemoveAllChildren() suppriment des nœuds. CompositeNode.IndexOf(child) renvoie la position d’un enfant. Sur Node lui-même, Clone(isCloneChildren) crée une copie — éventuellement profonde — et Remove() détache le nœud de son parent. Node.NextPreOrder(rootNode) et PreviousPreOrder(rootNode) parcourent l’arbre dans l’ordre du document, limité par un nœud racine donné, ce qui est une façon courante de balayer un sous-arbre entier nœud par nœud sans code récursif.
Collections de nœuds et notifications de changements
NodeCollection est une collection concrète et mutable de nœuds prenant en charge Add(node), Insert(index, node), Remove(node), RemoveAt(index), Clear(), Contains(node), IndexOf(node) et ToArray(), ainsi qu’une propriété Count. NodeList et NodeEnumerator prennent en charge l’itération de type lecture sur les résultats de requêtes, comme les nœuds renvoyés par SelectNodes. Pour observer les changements structurels au fur et à mesure qu’ils se produisent, implémentez INodeChangingCallback — ses méthodes NodeInserting(args), NodeInserted(args), NodeRemoving(args) et NodeRemoved(args) reçoivent chacune un NodeChangingArgs décrivant le Node affecté, son OldParent/NewParent, et le NodeChangingAction qui s’est produit.
Visiter les nœuds avec DocumentVisitor
Pour traiter chaque type de nœud de manière structurée sans parcourir manuellement l’arbre, sous-classez DocumentVisitor et surchargez les méthodes Visit*Start/Visit*End pour les types de nœuds qui vous intéressent — VisitParagraphStart/VisitParagraphEnd, VisitTableStart/VisitTableEnd, VisitRun, etc. — puis appelez Node.Accept(visitor) sur la racine du sous-arbre pour l’exécuter. C’est le modèle standard pour exporter, transformer ou inspecter un document sans écrire de traversée récursive personnalisée pour chaque cas d’utilisation.
Conseils et meilleures pratiques
- Utilisez
NodeType.AnyavecGetChildNodes(NodeType.Any, true)lorsque vous avez besoin de chaque descendant quel que soit son type, plutôt que d’appelerGetChildNodesune fois par type. - Privilégiez
Node.NextPreOrder(rootNode)/PreviousPreOrder(rootNode)pour un balayage plat et non récursif d’un sous-arbre plutôt que d’écrire une descente récursive manuelle. - Implémentez
DocumentVisitorplutôt que du code de parcours d’arbre ad hoc lorsque le même traitement doit s’exécuter sur de nombreux types de nœuds différents. - Vérifiez
Node.IsCompositeavant de le convertir enCompositeNodelorsque vous travaillez avec un nœud de type inconnu. - Enregistrez un
INodeChangingCallbacklorsque vous devez réagir aux insertions ou suppressions effectuées par d’autres code (y compris les opérations intégrées), et non seulement aux modifications apportées directement par votre propre code.
Problèmes courants
| Problème | Cause | Correction |
|---|---|---|
Le transtypage d’un Node en CompositeNode génère une erreur de cast invalide | Le nœud est d’un type feuille (par exemple Run ou FieldStart) qui ne peut pas contenir d’enfants | Vérifiez d’abord Node.IsComposite, ou utilisez NodeType pour confirmer que le nœud est de type conteneur avant le cast |
GetChildNodes ne renvoie que les enfants directs | isDeep a été laissé false (ou omis lorsque la surcharge par défaut est superficielle) | Passez isDeep: true pour rechercher tout le sous-arbre |
| Modifier une collection pendant son itération génère une erreur | Des nœuds ont été ajoutés ou supprimés à l’intérieur d’un foreach sur NodeCollection ou du résultat de GetChildNodes | Matérialisez d’abord les nœuds à modifier avec ToArray(), puis parcourez le tableau tout en modifiant l’arbre |
Une sous-classe DocumentVisitor n’est pas appelée pour certains contenus | Le visiteur a été invoqué via Accept() sur un nœud qui ne contient pas le contenu cible, ou la méthode Visit* correspondante n’a pas été surchargée | Appelez Accept() sur un ancêtre qui contient le contenu, et surchargez chaque paire Visit*Start/Visit*End pertinente aux types de nœuds traités |
FAQ
Quelle est la différence entre Node et CompositeNode ?
Node est la classe de base dont dérive chaque élément de document. CompositeNode étend Node pour les éléments pouvant contenir des enfants (comme Body, Paragraph et Table); les éléments feuilles (comme Run) dérivent directement de Node et ne peuvent pas contenir d’enfants.
Comment trouver tous les nœuds d’un type spécifique dans un document?
Appelez CompositeNode.GetChildNodes(nodeType, isDeep) sur le nœud qui sert de racine à votre recherche (souvent le Document lui-même), en passant le NodeType souhaité et isDeep: true pour rechercher dans tout le sous-arbre, pas seulement les enfants directs.
Comment supprimer des nœuds en toute sécurité pendant l’itération?
Collectez d’abord les nœuds à supprimer dans un tableau — par exemple avec NodeCollection.ToArray() — puis parcourez ce tableau et appelez Remove() sur chaque nœud, au lieu de modifier la collection active pendant qu’un foreach l’itère.
Que sont les nœuds FieldStart, FieldSeparator et FieldEnd?
Ce sont des valeurs NodeType marquant la mécanique d’un champ Word intégré au flux d’exécution: FieldStart et FieldEnd délimitent le champ, et FieldSeparator sépare le code du champ de son résultat actuellement affiché.
Comment traiter chaque paragraphe, tableau et exécution dans un document?
Sous-classez DocumentVisitor, surchargez VisitParagraphStart/VisitParagraphEnd, VisitTableStart/VisitTableEnd et VisitRun pour les types de nœuds dont vous avez besoin, puis appelez Accept(visitor) sur le Document (ou toute racine de sous-arbre) pour l’exécuter.
API Reference Résumé
| Classe/Méthode | Description |
|---|---|
Node | Classe de base abstraite pour chaque nœud d’un arbre de document |
CompositeNode | Classe de base pour les nœuds pouvant contenir des enfants |
CompositeNode.GetChildNodes(nodeType, isDeep) | Récupérer tous les enfants d’un type donné, éventuellement dans l’ensemble du sous-arbre |
CompositeNode.AppendChild(newChild) / InsertBefore(newChild, refChild) | Ajouter un nœud à un conteneur à la fin, ou avant/après un enfant existant |
NodeType | Enum identifiant le type d’un nœud (Paragraph, Table, Run, FieldStart, et ainsi de suite) |
NodeCollection | Collection concrète et mutable d’objets Node |
Node.Clone(isCloneChildren) | Copier un nœud, éventuellement en incluant ses enfants |
Node.NextPreOrder(rootNode) / PreviousPreOrder(rootNode) | Parcourir un sous-arbre dans l’ordre du document |
INodeChangingCallback | Interface de rappel notifiée lorsque des nœuds sont insérés ou supprimés |
DocumentVisitor | Classe de base pour un parcours structuré, dispatché par type via Node.Accept(visitor) |