Cómo trabajar con nodos en .NET
Esta guía muestra cómo recorrer, consultar y modificar el árbol de nodos de un documento Word directamente en Aspose.Words FOSS para .NET, usando el Node and CompositeNode clases base que Document, Section, Paragraph, Table, y sobre las cuales se construyen todos los demás elementos del documento. Cubre la delimitación de una búsqueda por NodeType, añadiendo y eliminando nodos programáticamente, reaccionando a cambios estructurales y usando DocumentVisitor para el recorrido despachado por tipo.
Guía paso a paso
Paso 1: Instalar el paquete
Aspose.Words FOSS for .NET aún no publica un paquete NuGet – construya la biblioteca desde el código fuente en su lugar. Clone https://github.com/aspose-words-foss/Aspose.Words-FOSS-for-.NET.git, luego compile Aspose.Words.sln en configuración Release con el SDK .NET y haga referencia al ensamblado Aspose.Words resultante desde su proyecto. La biblioteca tiene como objetivo .NET Standard 2.0, por lo que se ejecuta en .NET Framework 4.6.2+ y .NET 6/8/10.
Paso 2: Importar las clases requeridas
Agregue una directiva using Aspose.Words; – Node, CompositeNode, NodeType, NodeCollection, NodeList, INodeChangingCallback y DocumentVisitor se encuentran en el espacio de nombres Aspose.Words junto a Document.
Paso 3: Entender el nodo y las clases base CompositeNode
Cada objeto en un árbol de documentos deriva de la clase abstracta Node, que expone NodeType (identificando qué tipo de nodo es), ParentNode (el CompositeNode contenedor), Document (el documento propietario), y PreviousSibling / NextSibling para moverse entre nodos al mismo nivel. Consulte Node.IsComposite para averiguar si un nodo dado puede contener hijos antes de convertirlo. CompositeNode extiende Node para elementos contenedores – FirstChild, LastChild y Count describen sus hijos inmediatos, y HasChildNodes informa si existen.
Paso 4: Consultar hijos por tipo
Llame a CompositeNode.GetChildNodes(nodeType, isDeep) para obtener cada hijo de un NodeType determinado, pasando isDeep: true para buscar en todo el subárbol en lugar de solo en los hijos directos, o NodeType.Any para hacer coincidir cualquier tipo de nodo sin importar su clase. CompositeNode.GetChild(nodeType, index, isDeep) recupera un único hijo coincidente por índice. Para los tipos de nodo estructurales (Document, Section, Body, Paragraph, Table, Row, Cell), los tipos de contenido en línea (Run, Shape, Comment), y los tipos de maquinaria de campo (FieldStart, FieldSeparator, FieldEnd, que marcan los límites de un campo de Word y el texto de conmutación en el flujo de ejecución), pase el valor específico NodeType que identifica lo que busca.
Paso 5: Ejecutar consultas XPath a través de un subárbol
CompositeNode también implementa SelectNodes(xpath) y SelectSingleNode(xpath) para consultas basadas en XPath a través del subárbol, devolviendo coincidencias como un NodeList (o un único Node). CompositeNode.GetEnumerator() admite un simple foreach sobre los hijos directos de un nodo cuando una consulta XPath es más de lo que la tarea necesita.
Paso 6: Añadir, mover y eliminar nodos
Para modificar el árbol, llame a AppendChild(newChild) o PrependChild(newChild) para añadir un nodo al inicio o al final de los hijos de un contenedor, y InsertBefore(newChild, refChild) o InsertAfter(newChild, refChild) para insertar relativo a un hijo existente. RemoveChild(oldChild) elimina un hijo, y RemoveAllChildren() borra todos los hijos de una vez; CompositeNode.IndexOf(child) informa la posición de un hijo. Sobre Node mismo, Clone(isCloneChildren) copia un nodo — opcionalmente incluyendo sus hijos — y Remove() separa el nodo de su padre.
Paso 7: Materializar nodos antes de mutar una colección en vivo
Modificar una colección mientras un bucle foreach está iterando sobre ella – ya sea un NodeCollection o el resultado de GetChildNodes – genera un error. Llama a ToArray() sobre la colección primero para materializar los nodos en una matriz fija, luego itera sobre esa matriz mientras llamas a Remove() o haces otros cambios en el árbol.
Paso 8: Escanear un subárbol en orden de documento
Node.NextPreOrder(rootNode) y PreviousPreOrder(rootNode) recorren el árbol en orden de documento, limitados por un nodo raíz dado – una forma común de escanear un subárbol completo nodo por nodo sin escribir código de recorrido recursivo.
Paso 9: Procesar cada tipo de nodo con DocumentVisitor
Para procesar muchos tipos diferentes de nodos de forma estructurada, subclase la clase abstracta DocumentVisitor y sobrescribe los métodos Visit*Start / Visit*End para los tipos de nodo que importan – por ejemplo VisitParagraphStart / VisitParagraphEnd, VisitSectionStart / VisitSectionEnd, VisitBodyStart / VisitBodyEnd – luego llama a Node.Accept(visitor) en la raíz del subárbol para ejecutarlo. Este es el patrón estándar para exportar, transformar o inspeccionar un documento sin escribir recorridos recursivos personalizados para cada caso de uso.
Paso 10: Reaccionar a cambios estructurales de otro código
Implementa INodeChangingCallback cuando el código necesita reaccionar a inserciones o eliminaciones realizadas por otro código – incluidas operaciones integradas – no solo a cambios realizados directamente. Sus métodos NodeInserting(args), NodeInserted(args), NodeRemoving(args) y NodeRemoved(args) reciben cada uno un NodeChangingArgs que describe el Node afectado, su padre antiguo y nuevo, y el NodeChangingAction (Insert o Remove) que ocurrió.
Problemas comunes y soluciones
Convertir un Nodo a CompositeNode lanza un error de conversión inválida El nodo es de tipo hoja – por ejemplo Run or FieldStart – que no puede contener hijos. Verifique Node.IsComposite primero, o use NodeType para confirmar que el nodo es de tipo contenedor antes de hacer casting.
GetChildNodes devuelve hijos directos cuando isDeep es false isDeep se dejó false (o omitido donde la sobrecarga predeterminada es shallow). Pase isDeep: true to GetChildNodes() para buscar todo el subárbol en lugar de los hijos inmediatos.
Modificar una colección mientras se itera sobre ella arroja un error Se añadieron o eliminaron nodos dentro de un foreach sobre un NodeCollection o el resultado de GetChildNodes. Llama ToArray() primero para materializar los nodos a modificar, luego itere sobre esa matriz mientras muta el árbol.
Una subclase de DocumentVisitor no se llama para cierto contenido El visitante fue invocado a través de Accept() en un nodo que realmente no contiene el contenido objetivo, o el correspondiente Visit* método no fue sobrescrito. Llama Accept() en un ancestro que contiene el contenido, y sobrescribe cada Visit*Start / Visit*End par relevante a los tipos de nodo que se están procesando.
INodeChangingCallback no se notifica sobre un cambio El callback solo informa cambios en el Document contra el que se registró. Confirma que el callback está adjunto al correcto Document instancia antes de que ocurra el cambio, no después.
Preguntas frecuentes
¿Cuál es la diferencia entre Node y CompositeNode?
Node es la clase base de la que deriva cada elemento del documento. CompositeNode extiende Node para los elementos que pueden contener hijos (como Body, Paragraph y Table); los elementos hoja como Run derivan directamente de Node y no pueden contener hijos.
¿Cómo encuentro todos los nodos de un tipo específico en un documento?
Llama a CompositeNode.GetChildNodes(nodeType, isDeep) en el nodo que origina la búsqueda – a menudo el propio Document – pasando el NodeType objetivo y isDeep: true para buscar en todo el subárbol, no solo en los hijos directos.
¿Cómo elimino nodos de forma segura mientras itero?
Recopila los nodos a eliminar en una matriz primero – por ejemplo con NodeCollection.ToArray() – luego itera sobre esa matriz y llama a Remove() en cada nodo, en lugar de modificar la colección en vivo mientras un foreach la está iterando.
¿Qué son los nodos FieldStart, FieldSeparator y FieldEnd?
Son valores NodeType que marcan la maquinaria de un campo de Word incrustado en el flujo de ejecución: FieldStart y FieldEnd enlazan el campo, y FieldSeparator divide el código del campo de su resultado actualmente mostrado.
¿Cómo proceso cada párrafo, tabla y segmento en un documento?
Cree una subclase de DocumentVisitor, sobrescriba los métodos Visit*Start / Visit*End para los tipos de nodo necesarios, luego llame a Accept(visitor) en el Document (o en cualquier raíz de subárbol) para ejecutarlo.