Visor XML

 

Fecha: 13/Jul/2005 (10 de Julio de 2005)
Autor: Braulio Núñez Lanza

 

 


 

En este artículo se describe el proceso de desarrollo de una pequeña herramienta que permitirá formatear un documento XML haciendo que resulte más legible, mostrarlo mediante un árbol, y seleccionar nodos mediante XPath visualizando aquéllos que respondan a la expresión de búsqueda resaltados. Adicionalmente se le añadirán otras características secundarias como el guardar y cargar ficheros, una lista de archivos recientes y  ajuste de línea. Es una herramienta sencilla pero que puede ser bastante útil para trabajar con XML.

El resultado final de la aplicación será el siguiente:



Texto sin formatear


Texto formateado mostrando el resultado de una consulta.

Para empezar se creará un proyecto del tipo “Aplicación para Windows”, y en el formulario se insertarán todos los controles que requerirá la interfaz.

 

 

FORMATEANDO EL DOCUMENTO XML:

 

El código para formatear el texto se basa en dos funciones. La primera  de ellas se llama FormatearTexto y se encargará de inicializar las variables, cargar el texto introducido en el primer RichTextBox en una variable del tipo XmlDocument. Esta primera función llamará a la segunda (IndentarXML), la cual es una función recursiva que se llamará así misma por cada nodo que tenga el arbol XML.

Private Sub FormatearTexto(ByVal consultar As Boolean)

        Dim arbol As New Xml.XmlDocument

        Dim profundidad As Integer

        Dim cad As String

 

        'Utilizados solo para hacer la consulta

        Dim nodosSeleccionados As Xml.XmlNodeList

        Dim inicioNodosSeleccionados As ArrayList

        Dim finalNodosSeleccionados As ArrayList

        Dim i As Integer

 

        Try

            arbol.LoadXml(rtxtXML.Text)

 

            profundidad = 0

            cad = ""

            rtxtTextoFormateado.Text = ""

 

            If arbol.FirstChild.NodeType = Xml.XmlNodeType.XmlDeclaration Then

                cad &= arbol.FirstChild.OuterXml & vbCr

            End If

            If consultar Then

                nodosSeleccionados = arbol.SelectNodes(txtConsulta.Text)

                inicioNodosSeleccionados = New ArrayList

                finalNodosSeleccionados = New ArrayList

                IndentarXML(arbol.DocumentElement, cad, 0, nodosSeleccionados, inicioNodosSeleccionados, finalNodosSeleccionados)

                rtxtTextoFormateado.Text = cad

                For i = 0 To inicioNodosSeleccionados.Count - 1

                    rtxtTextoFormateado.Select(CInt(inicioNodosSeleccionados(i)), CInt(finalNodosSeleccionados(i)) - CInt(inicioNodosSeleccionados(i)))

                    rtxtTextoFormateado.SelectionColor = Color.Blue

                    rtxtTextoFormateado.SelectionFont = fuenteNegritaRTXT

                Next

            Else

                IndentarXML(arbol.DocumentElement, cad, profundidad)

                rtxtTextoFormateado.Text = cad

            End If

        Catch ex As Exception

            MsgBox("Se ha producido el siguiente error:" & vbCrLf & vbCrLf & " - " & ex.Message)

        End Try

    End Sub

 

    Private Function IndentarXML(ByVal nodo As Xml.XmlNode, ByRef cad As String, ByVal profundidad As Integer, Optional ByVal nodosSeleccionados As Xml.XmlNodeList = Nothing, Optional ByVal inicioNodosSeleccionados As ArrayList = Nothing, Optional ByVal finalNodosSeleccionados As ArrayList = Nothing) As String

        Dim hijo As Xml.XmlNode

        Dim atr As Xml.XmlAttribute

 

        If nodo.NodeType <> Xml.XmlNodeType.Text And nodo.NodeType <> Xml.XmlNodeType.Comment Then

            cad &= New String(Chr(9), profundidad)

 

            If Not nodosSeleccionados Is Nothing AndAlso estaSeleccionado(nodo, nodosSeleccionados) Then

                If cad = "" Then

                    inicioNodosSeleccionados.Add(0)

                Else

                    inicioNodosSeleccionados.Add(cad.Length - 1)

                End If

 

            End If

 

            cad &= "<" & nodo.Name

            For Each atr In nodo.Attributes

                cad &= " " & atr.Name & "=""" & atr.Value & """"

            Next

            cad &= ">"

 

            If Not nodosSeleccionados Is Nothing AndAlso estaSeleccionado(nodo, nodosSeleccionados) Then

                finalNodosSeleccionados.Add(cad.Length)

            End If

 

            cad &= textoDelNodo(nodo)

 

            If tieneHijos(nodo) Then

                cad &= vbCr

                For Each hijo In nodo.ChildNodes

                    IndentarXML(hijo, cad, profundidad + 1, nodosSeleccionados, inicioNodosSeleccionados, finalNodosSeleccionados)

                Next

                cad &= New String(Chr(9), profundidad)

            End If

            cad &= "</" & nodo.Name & ">" & vbCr

        End If

    End Function

 

La función FormatearTexto recibe un parámetro que indica si se está realizando una consulta o no. Si es así, se obtendrán todos los nodos seleccionados por la consulta y se crearán dos arrayList para almacenar las posiciones iniciales y finales de los nodos seleccionados, para poder posteriormente resaltarlos en el RichTextBox.

La función IndentarXML se encarga de transformar el nodo en cadena formateada y de llamarse a si misma recursivamente por cada hijo que tuviera el nodo. El parámetro profundidad se va incrementando en uno en cada nivel del árbol, y sirve para tabular correctamente el texto de cada nodo. Si se esta consultando y el nodo actual pertenece a los nodos seleccionados por la consulta XPath, entonces se marcará su posición inicial y final añadiéndolas a los arrayList.

 

 

POBLANDO EL TREEVIEW:

 

El proceso es muy similar al descrito anteriormente, salvo que ahora en vez de transformar el árbol en una cadena de texto formateada, se irán incluyendo sus nodos jerárquicamente en un TreeView.

Para poblar el TreeView también nos basaremos en dos funciones, una para inicializar las variables y cargar el documento xml, y otra que será llamada de forma recursiva.

 

Private Sub MostrarArbol(ByVal consultar As Boolean)

        Dim arbol As New Xml.XmlDocument

        Dim nodoRaiz As TreeNode

 

        'Utilizados solo para hacer la consulta

        Dim nodosSeleccionados As Xml.XmlNodeList

 

        Try

            arbol.LoadXml(rtxtXML.Text)

 

            trvXML.Nodes.Clear()

            nodoRaiz = New TreeNode(arbol.DocumentElement.Name)

            trvXML.Nodes.Add(nodoRaiz)

 

            If consultar Then

                nodosSeleccionados = arbol.SelectNodes(txtConsulta.Text)

                If estaSeleccionado(arbol.DocumentElement, nodosSeleccionados) Then

                    nodoRaiz.ForeColor = Color.Blue

                    nodoRaiz.NodeFont = fuenteNegritaArbol

                End If

                anadirNodosAArbol(nodoRaiz, arbol.DocumentElement, nodosSeleccionados)

            Else

                anadirNodosAArbol(nodoRaiz, arbol.DocumentElement)

            End If

 

            trvXML.ExpandAll()

 

        Catch ex As Exception

            MsgBox("Se ha producido el siguiente error:" & vbCrLf & vbCrLf & " - " & ex.Message)

        End Try

 

    End Sub

 

    Private Sub AnadirNodosAArbol(ByVal arbol As TreeNode, ByVal nodo As Xml.XmlNode, Optional ByVal nodosSeleccionados As Xml.XmlNodeList = Nothing)

        Dim hijo As Xml.XmlNode

        Dim atr As Xml.XmlAttribute

        Dim nodoArbol As TreeNode

 

        For Each hijo In nodo.ChildNodes

            If hijo.NodeType <> Xml.XmlNodeType.Text And hijo.NodeType <> Xml.XmlNodeType.Comment Then

                nodoArbol = New TreeNode(hijo.Name)

                If Not nodosSeleccionados Is Nothing Then

                    If estaSeleccionado(hijo, nodosSeleccionados) Then

                        nodoArbol.ForeColor = Color.Blue

                        nodoArbol.NodeFont = fuenteNegritaArbol

                    End If

                End If

                arbol.Nodes.Add(nodoArbol)

                If textoDelNodo(hijo) <> "" Then

                    nodoArbol.Text &= "=" & textoDelNodo(hijo)

                End If

                For Each atr In hijo.Attributes

                    nodoArbol.Text &= "   " & atr.Name & "=" & atr.Value

                Next

 

                anadirNodosAArbol(nodoArbol, hijo, nodosSeleccionados)

            End If

        Next

    End Sub

COMPLETANDO LA HERRAMIENTA:

 

Con esto se habrá terminado el núcleo de nuestra aplicación, habiéndola dotado de la funcionalidad básica para la que se ideó, es decir, el mostrar el xml formateado para que resulte más legible, mostrarlo mediante un árbol y permitir seleccionar nodos mediante XPath visualizando aquellos que respondan a la expresión de búsqueda resaltados.  

Vayamos ahora a dotarla de ciertos elementos adicionales para completar la aplicación.

En primer lugar le añadiremos la funcionalidad de permitir ajustar las líneas para que todo el texto de una línea se muestre en pantalla, ocupando las líneas que sean necesarias para mostrarse. Esto es tan sencillo como añadir un checkBox, y establecer la propiedad WordWrap de los RichTextBox al valor de la propiedad Checked del checkBox

Le añadiremos también la funcionalidad de cargar y guardar ficheros. Con este fin se crearán dos elementos de menú y se les asociarán teclas de acceso rápido, CTRL+C y CTRL+G respectivamente. El documento que guardaremos será el xml ya formateado, no el original.

Según vayamos cargando ficheros, éstos se irán incluyendo en la lista de recientes hasta un máximo de cinco. De igual forma incluimos en la lista de ficheros recientes aquéllos documentos que guardemos. El código que realiza esta inserción es el siguiente:

Private Sub InsertarEnRecientes(ByVal nombreFichero As String)

        Dim menu As MenuItem

        Dim i As Integer

        Dim encontrado As Boolean

 

        Try

            i = 0

            While i <= mnuRecientes.MenuItems.Count - 1 And Not encontrado

                'Si ya estaba lo quitamos

                If mnuRecientes.MenuItems(i).Text = nombreFichero Then

                    mnuRecientes.MenuItems.RemoveAt(i)

                    encontrado = True

                End If

                i = i + 1

            End While

            menu = New MenuItem(nombreFichero, AddressOf FichReciente_Click)

            mnuRecientes.MenuItems.Add(0, menu)

            If mnuRecientes.MenuItems.Count = 6 Then

                mnuRecientes.MenuItems.RemoveAt(5)

            End If

        Catch ex As Exception

            MsgBox("Se ha producido el siguiente error:" & vbCrLf & vbCrLf & " - " & ex.Message)

        End Try

 

    End Sub

 

Cuando se cierre el formulario, se guardará la lista de ficheros recientes en un fichero XML, para que dicha listas persista entre ejecuciones de la aplicación.

 

    Private Sub GuardarRecientes()

        Dim arbol As New Xml.XmlDocument

        Dim nodo As Xml.XmlNode

        Dim menu As MenuItem

 

        Try

            nodo = arbol.CreateElement("Recientes")

            arbol.AppendChild(nodo)

 

            For Each menu In mnuRecientes.MenuItems

                nodo = arbol.CreateElement("Fichero")

                nodo.InnerText = menu.Text

                arbol.DocumentElement.AppendChild(nodo)

            Next

 

            arbol.Save(Application.StartupPath & "\Recientes.xml")

 

        Catch ex As Exception

            MsgBox("Se ha producido el siguiente error:" & vbCrLf & vbCrLf & " - " & ex.Message)

        End Try

    End Sub

 

Finalmente, agregaremos un formulario para mostrar una ventana con la información sobre la aplicación, la típica “Acerca de…”

 


Espacios de nombres usados en el código de este artículo:

System.Sml
System.Windows.Forms

 


Fichero con el código de ejemplo: bnlbnl_visorXML.zip - Tamaño KB


ir al índice