Cómo... en .NET
 

Clase para manejar los datos de configuración

Publicado el 17/Ago/2006
Actualizado el 12/Ene/2008
Autor: Guillermo 'guille' Som

Esta clase sirve para acceder y modificar ficheros de configuración, para que los manejes al estilo de los INI, pero con formato XML. El código usado es para las versiones de Visual Studio .NET (2002 / 2003), aunque es válido para cualquier versión. Pulsa en los siguientes links si quieres la misma clase pero para Visual Basic 2005 o para Visual C# 2005.

 

 

Introducción:

Esta clase (ConfigXml) es la versión para Visual Studio .NET 2003 de la que ya publiqué para Visual Studio 2005 (versión de Visual Basic y versión para C#), para ver con detalle que funcionalidad ofrece esta clase, pulsa en cualquiera de los dos links anteriores.

Actualizado 12/Ene/2008: He publicado una actualización del código para Visual Basic 2005/2008

IMPORTANTE:
Lo que si te quiero aclarar, es que esta clase NO se puede usar para leer y guardar los datos generados por el Visual Studio en el fichero app.config, si quieres esa funcionalidad (o casi), puedes usar la versión anterior.

Tal como te digo en la advertencia anterior, esta clase NO vale para compatibilizar los ficheros creados por Visual Studio, sino para crear tus propios ficheros en los que guardar los datos de configuración.
El formato usado es XML guardados con la codificación UTF-8, por tanto si la modificas manualmente asegúrate de que se guarda en ese formato, sino seguramente recibirás un error al acceder al fichero, esto no es fallo de la clase, es algo que ocurre siempre que trabajemos con el formato UTF-8 y después no lo guardemos en ese formato.

La diferencia principal con la versión para Visual Studio 2005, es que en aquella se usan colecciones de tipo generic para devolver las secciones, y en esta versión y debido a que Visual Studio .NET 2003 no sabe nada de generics, pues utilizo colecciones de tipo StringCollection para devolver las secciones (método Secciones) y una colección de tipo StringDictionary para devolver las claves y valores de la sección indicada (método Claves).
Por lo demás, el código es exactamente el mismo que para las versiones de los hermanos mayores.

En el ZIP puedes ver el código completo de un ejemplo de cómo usar la clase, (con comentarios más o menos detallados de lo que se hace en cada caso), tanto para Visual Basic .NET 2003 como para Visual C# 2003, además el código fuente de la clase te lo muestro un poco más abajo, también para Visual Basic como para C#.

Por supuesto, ese mismo código es válido para cualquier versión de .NET, aunque si quieres usar las clases especialmente creadas para la versión 2005 de Visual Studio, usa los links que hay al principio.

 

Como de costumbre, espero que te sea de utilidad.

Nos vemos.
Guillermo

P.S.
Esta clase la he usado por primera vez en la utilidad para "filtrar" los ficheros de Word para usar en las colaboraciones, al menos para usarla con Visual Studio .NET 2003.

 

Nota (14/Sep/07):
Si quieres guardar correctamente tipos de datos Font y Color, puedes usar la actualización para Visual Basic que ha hecho Antonio R. Fernandez.

 


Código para Visual Basic.NET (VB.NET)El código para Visual Basic .NET (cualquier versión)

'------------------------------------------------------------------------------
' Clase para manejar ficheros de configuración                      (17/Ago/06)
'
' Versión para Visual Basic 2003 basada en la clase de Visual Basic 2005
'
' ©Guillermo 'guille' Som, 2005-2006
'------------------------------------------------------------------------------
Option Explicit On 
Option Strict On

Imports Microsoft.VisualBasic
Imports System

Imports System.Collections
Imports System.Collections.Specialiced
Imports System.Configuration
Imports System.Xml
Imports System.IO

Namespace elGuille.info.Util

Public Class ConfigXml
 
    '----------------------------------------------------------------------
    ' Los campos y métodos privados
    '----------------------------------------------------------------------
    Private mGuardarAlAsignar As Boolean = True
    Private Const configuration As String = "configuration/"
    Private ficConfig As String = ""
    Private configXml As New XmlDocument
    '
    ''' <summary>
    ''' Indica si se se guardarán los datos cuando se añadan nuevos.
    ''' </summary>
    Public Property GuardarAlAsignar() As Boolean
        Get
            Return mGuardarAlAsignar
        End Get
        Set(ByVal value As Boolean)
            mGuardarAlAsignar = value
        End Set
    End Property
    '
    ''' <summary>
    ''' Obtiene un valor de tipo cadena de la sección y clave indicadas.
    ''' </summary>
    Public Function GetValue(ByVal seccion As String, ByVal clave As String) As String
        Return GetValue(seccion, clave, "")
    End Function
    ''' <summary>
    ''' Obtiene un valor de tipo cadena de la sección y clave indicadas.
    ''' </summary>
    Public Function GetValue(ByVal seccion As String, _
                            ByVal clave As String, _
                            ByVal predeterminado As String) As String
        Return cfgGetValue(seccion, clave, predeterminado)
    End Function
    ''' <summary>
    ''' Obtiene un valor de tipo entero de la sección y clave indicadas.
    ''' </summary>
    Public Function GetValue(ByVal seccion As String, _
                            ByVal clave As String, _
                            ByVal predeterminado As Integer) As Integer
        Return CInt(cfgGetValue(seccion, clave, predeterminado.ToString))
    End Function
    ''' <summary>
    ''' Obtiene un valor de tipo boolean de la sección y clave indicadas.
    ''' </summary>
    Public Function GetValue(ByVal seccion As String, _
                            ByVal clave As String, _
                            ByVal predeterminado As Boolean) As Boolean
        Dim def As String = "0"
        If predeterminado Then def = "1"
        def = cfgGetValue(seccion, clave, def)
        If def = "1" Then
            Return True
        Else
            Return False
        End If
    End Function
 
    ''' <summary>
    ''' Asignar un valor de tipo cadena en la sección y clave indicadas.
    ''' </summary>
    Public Sub SetValue(ByVal seccion As String, _
			ByVal clave As String, ByVal valor As String)
        cfgSetValue(seccion, clave, valor)
    End Sub
 
    ''' <summary>
    ''' Asignar un valor de tipo entero en la sección y clave indicadas.
    ''' </summary>
    Public Sub SetValue(ByVal seccion As String, _
			ByVal clave As String, ByVal valor As Integer)
        cfgSetValue(seccion, clave, valor.ToString)
    End Sub
 
    ''' <summary>
    ''' Asignar un valor de tipo boolean en la sección y clave indicadas.
    ''' </summary>
    Public Sub SetValue(ByVal seccion As String, _
			ByVal clave As String, ByVal valor As Boolean)
        If valor Then
            cfgSetValue(seccion, clave, "1")
        Else
            cfgSetValue(seccion, clave, "0")
        End If
    End Sub
 
    ''' <summary>
    ''' Asigna un valor de tipo cadena en la sección y clave indicadas.
    ''' </summary>
    Public Sub SetKeyValue(ByVal seccion As String, _
			ByVal clave As String, ByVal valor As String)
        cfgSetKeyValue(seccion, clave, valor)
    End Sub
 
    ''' <summary>
    ''' Asigna un valor de tipo entero en la sección y clave indicadas.
    ''' </summary>
    Public Sub SetKeyValue(ByVal seccion As String, _
			ByVal clave As String, ByVal valor As Integer)
        cfgSetKeyValue(seccion, clave, valor.ToString)
    End Sub
 
    ''' <summary>
    ''' Asigna un valor de tipo boolean en la sección y clave indicadas.
    ''' </summary>
    Public Sub SetKeyValue(ByVal seccion As String, _
			ByVal clave As String, ByVal valor As Boolean)
        If valor Then
            cfgSetKeyValue(seccion, clave, "1")
        Else
            cfgSetKeyValue(seccion, clave, "0")
        End If
    End Sub
 
    ''' <summary>
    ''' Elimina la sección indicada, aunque en realidad la deja vacía.
    ''' </summary>
    Public Sub RemoveSection(ByVal seccion As String)
        Dim n As XmlNode
        n = configXml.SelectSingleNode(configuration & seccion)
        If Not n Is Nothing Then
            n.RemoveAll()
            If mGuardarAlAsignar Then
                Me.Save()
            End If
        End If
    End Sub
 
    ' Guardar el fichero de configuración
    ' 
    ''' <summary>
    ''' Guardar el fichero de configuración.
    ''' </summary>
    Public Sub Save()
        configXml.Save(ficConfig)
    End Sub
 
    ''' <summary>
    ''' Lee el fichero de configuración.
    ''' </summary>
    Public Sub Read()
        Dim fic As String = ficConfig
        Const revDate As String = "Thu, 17 Aug 2006 20:20:00 GMT"
        If File.Exists(fic) Then
            configXml.Load(fic)
            ' Actualizar los datos de la información de esta clase
            Dim b As Boolean = mGuardarAlAsignar
            mGuardarAlAsignar = False
            Me.SetValue("configXml_Info", "info", _
			"Generado con Config para Visual Basic 2003")
            Me.SetValue("configXml_Info", "revision", revDate)
            Me.SetValue("configXml_Info", "formatoUTF8", _
			"El formato de este fichero debe ser UTF-8")
            mGuardarAlAsignar = b
            Me.Save()
        Else
            ' Crear el XML de configuración con la sección General
            Dim sb As New System.Text.StringBuilder
            sb.Append("<?xml version=""1.0"" encoding=""utf-8"" ?>")
            sb.Append("<configuration>")
            ' Por si es un fichero appSetting
            sb.Append("<configSections>")
            sb.Append("<section name=""General"" " & _
			"type=""System.Configuration.DictionarySectionHandler"" />")
            sb.Append("</configSections>")
            sb.Append("<General>")
            sb.Append("<!-- Los valores irán dentro del elemento indicado por la clave -->")
            sb.Append("<!-- Aunque también se podrán indicar como pares key / value -->")
            sb.AppendFormat("<add key=""Revisión"" value=""{0}"" />", revDate)
            sb.Append("<!-- La clase siempre los añade como un elemento -->")
            sb.Append("<Copyright>©Guillermo 'guille' Som, 2005-2006</Copyright>")
            sb.Append("</General>")
            '
            sb.AppendFormat("<configXml_Info>{0}", vbCrLf)
            sb.AppendFormat("<info>Generado con Config para Visual Basic 2003</info>{0}", _
			vbCrLf)
            sb.AppendFormat("<copyright>©Guillermo 'guille' Som, 2005-2006" & _
			"</copyright>{0}", _
			vbCrLf)
            sb.AppendFormat("<revision>{0}</revision>{1}", revDate, vbCrLf)
            sb.AppendFormat("<formatoUTF8>El formato de este fichero debe ser UTF-8" & _
			"</formatoUTF8>{0}", _
			vbCrLf)
            sb.AppendFormat("</configXml_Info>{0}", vbCrLf)
            '
            sb.Append("</configuration>")
            ' Asignamos la cadena al objeto
            configXml.LoadXml(sb.ToString)
            '
            ' Guardamos el contenido de configXml y creamos el fichero
            configXml.Save(ficConfig)
        End If
    End Sub
 
    ''' <summary>
    ''' El nombre del fichero de configuración.
    ''' </summary>
    Public Property FileName() As String
        Get
            Return ficConfig
        End Get
        Set(ByVal value As String)
            ' Al asignarlo, NO leemos el contenido del fichero
            ficConfig = value
            'LeerFile()
        End Set
    End Property
 
    ''' <summary>
    ''' Constructor en el que indicamos el nombre del fichero de configuración.
    ''' </summary>
    Public Sub New(ByVal fic As String)
        ficConfig = fic
        ' Por defecto se guarda al asignar los valores
        mGuardarAlAsignar = True
        Read()
    End Sub
 
    ' Con este constructor podemos decidir si guardamos o no automáticamente
    ''' <summary>
    ''' Constructor en el que indicamos el nombre del fichero de configuración.
    ''' </summary>
    Public Sub New(ByVal fic As String, ByVal guardarAlAsignar As Boolean)
        ficConfig = fic
        mGuardarAlAsignar = guardarAlAsignar
        Read()
    End Sub
    '
    ''' <summary>
    ''' Devuelve una colección de tipo StringCollection
   ''' con las secciones del fichero de configuración.
    ''' </summary>
    Public Function Secciones() As StringCollection
        Dim d As New StringCollection
        Dim root As XmlNode
        Dim s As String = "configuration"
        root = configXml.SelectSingleNode(s)
        If Not root Is Nothing Then
            For Each n As XmlNode In root.ChildNodes
                d.Add(n.Name)
            Next
        End If
        Return d
    End Function
 
    ''' <summary>
    ''' Devuelve una colección de tipo StringDictionary
    ''' con las claves y valores de la sección indicada.
    ''' </summary>
     Public Function Claves(ByVal seccion As String) As StringDictionary
        Dim d As New StringDictionary
        Dim root As XmlNode
        seccion = seccion.Replace(" ", "_")
        root = configXml.SelectSingleNode(configuration & seccion)
        If Not root Is Nothing Then
            For Each n As XmlNode In root.ChildNodes
                If d.ContainsKey(n.Name) = False Then
                    d.Add(n.Name, n.InnerText)
                End If
            Next
        End If
        Return d
    End Function
    '
    '----------------------------------------------------------------------
    ' Los métodos privados
    '----------------------------------------------------------------------
    '
    ' El método interno para guardar los valores
    ' Este método siempre guardará en el formato:
    ' <seccion><clave>valor</clave></seccion>
    Private Sub cfgSetValue( _
                    ByVal seccion As String, _
                    ByVal clave As String, _
                    ByVal valor As String)
        '
        Dim n As XmlNode
        '
        ' Filtrar los caracteres no válidos
        ' en principio solo comprobamos el espacio
        seccion = seccion.Replace(" ", "_")
        clave = clave.Replace(" ", "_")
 
        ' Se comprueba si es un elemento de la sección:
        '   <seccion><clave>valor</clave></seccion>
        n = configXml.SelectSingleNode(configuration & seccion & "/" & clave)
        If Not n Is Nothing Then
            n.InnerText = valor
        Else
            Dim root As XmlNode
            Dim elem As XmlElement
            root = configXml.SelectSingleNode(configuration & seccion)
            If root Is Nothing Then
                ' Si no existe el elemento principal,
                ' lo añadimos a <configuration>
                elem = configXml.CreateElement(seccion)
                configXml.DocumentElement.AppendChild(elem)
                root = configXml.SelectSingleNode(configuration & seccion)
            End If
            If Not root Is Nothing Then
                ' Crear el elemento
                elem = configXml.CreateElement(clave)
                elem.InnerText = valor
                ' Añadirlo al nodo indicado
                root.AppendChild(elem)
            End If
        End If
        '
        If mGuardarAlAsignar Then
            Me.Save()
        End If
    End Sub
 
    ' Asigna un atributo a una sección
    ' Por ejemplo: <Seccion clave=valor>...</Seccion>
    ' También se usará para el formato de appSettings: <add key=clave value=valor />
    '   Aunque en este caso, debe existir el elemento a asignar.
    Private Sub cfgSetKeyValue( _
                    ByVal seccion As String, _
                    ByVal clave As String, _
                    ByVal valor As String)
        '
        Dim n As XmlNode
        '
        ' Filtrar los caracteres no válidos
        ' en principio solo comprobamos el espacio
        seccion = seccion.Replace(" ", "_")
        clave = clave.Replace(" ", "_")
 
        n = configXml.SelectSingleNode(configuration & seccion & _
				"/add[@key=""" & clave & """]")
        If Not n Is Nothing Then
            n.Attributes("value").InnerText = valor
        Else
            Dim root As XmlNode
            Dim elem As XmlElement
            root = configXml.SelectSingleNode(configuration & seccion)
            If root Is Nothing Then
                ' Si no existe el elemento principal,
                ' lo añadimos a <configuration>
                elem = configXml.CreateElement(seccion)
                configXml.DocumentElement.AppendChild(elem)
                root = configXml.SelectSingleNode(configuration & seccion)
            End If
            If Not root Is Nothing Then
                Dim a As XmlAttribute = CType( _
				configXml.CreateNode(XmlNodeType.Attribute, _
				clave, Nothing), _
                                     XmlAttribute)
                a.InnerText = valor
                root.Attributes.Append(a)
            End If
        End If
        If mGuardarAlAsignar Then
            Me.Save()
        End If
    End Sub
 
    ' Devolver el valor de la clave indicada
    Private Function cfgGetValue( _
                    ByVal seccion As String, _
                    ByVal clave As String, _
                    ByVal valor As String _
                    ) As String
        '
        Dim n As XmlNode
        '
        ' Filtrar los caracteres no válidos
        ' en principio solo comprobamos el espacio
        seccion = seccion.Replace(" ", "_")
        clave = clave.Replace(" ", "_")
        ' Primero comprobar si están el formato de appSettings:
        ' <add key = clave value = valor />
        n = configXml.SelectSingleNode(configuration & _
			seccion & "/add[@key=""" & clave & """]")
        If Not n Is Nothing Then
            Return n.Attributes("value").InnerText
        End If
        '
        ' Después se comprueba si está en el formato <Seccion clave = valor>
        n = configXml.SelectSingleNode(configuration & seccion)
        If Not n Is Nothing Then
            Dim a As XmlAttribute = n.Attributes(clave)
            If Not a Is Nothing Then
                Return a.InnerText
            End If
        End If
        '
        ' Por último se comprueba si es un elemento de seccion:
        '   <seccion><clave>valor</clave></seccion>
        n = configXml.SelectSingleNode(configuration & seccion & "/" & clave)
        If Not n Is Nothing Then
            Return n.InnerText
        End If
        '
        ' Si no existe, se devuelve el valor predeterminado
        Return valor
    End Function
End Class

End Namespace

 


Código para C Sharp (C#)El código para C# (cualquier versión)

//-----------------------------------------------------------------------------
// Clase para manejar ficheros de configuración                     (17/Ago/06)
//
// Versión para Visual C# 2003 basada en la clase de Visual Basic .NET 2003
//
// Las secciones siempre estarán dentro de <configuration> y </configuration>
//
// ©Guillermo 'guille' Som, 2005-2006
//-----------------------------------------------------------------------------
using System;
 
using System.Collections;
using System.Collections.Specialized;
using System.Configuration;
using System.Xml;
using System.IO;
 
namespace elGuille.info.Util
{

public class ConfigXml
{

    //----------------------------------------------------------------------
    // Los campos y métodos privados
    //----------------------------------------------------------------------
    private bool mGuardarAlAsignar = true;
    const string configuration = "configuration/";
    private string ficConfig = "";
    private XmlDocument configXml = new XmlDocument();
    //
    /// <summary>
    /// Indica si se se guardarán los datos cuando se añadan nuevos.
    /// </summary>
    /// <value>
    /// Indica si se se guardarán los datos cuando se añadan nuevos.
    /// </value>
    /// <returns>
    /// Un valor verdadero o falso según el valor de la propiedad
    /// </returns>
    public bool GuardarAlAsignar 
    {
        get
        {
            return mGuardarAlAsignar;
        }
        set
        {
            mGuardarAlAsignar = value;
        }
    }  
    //
    /// <summary>
    /// Obtiene un valor de tipo cadena de la sección y clave indicadas.
    /// </summary>
    /// <param name="seccion">La sección de la que queremos obtener el valor
    /// </param>
    /// <param name="clave">La clave de la que queremos recuperar el valor
    /// </param>
    /// <returns>
    /// Un valor de tipo cadena con el valor de la sección y clave indicadas
    /// </returns>
    /// <remarks>
    /// Existe otra sobrecarga para indicar un valor predeterminado.
    /// Tanbién hay otras dos sobrecargas para valores enteros y boolean.
    /// </remarks>
    public string GetValue(string seccion, string clave) 
    {
        return GetValue(seccion, clave, "");
    }  
    /// <summary>
    /// Obtiene un valor de tipo cadena de la sección y clave indicadas.
    /// </summary>
    /// <param name="seccion">La sección de la que queremos obtener el valor
    /// </param>
    /// <param name="clave">La clave de la que queremos recuperar el valor
    /// </param>
    /// <param name="predeterminado">
    /// El valor predeterminado para cuando no exista.
    /// </param>
    /// <returns>
    /// Un valor de tipo cadena con el valor de la sección y clave indicadas
    /// </returns>
    public string GetValue(string seccion, string clave, string predeterminado) 
    {
        return cfgGetValue(seccion, clave, predeterminado);
    }  
    /// <summary>
    /// Obtiene un valor de tipo entero de la sección y clave indicadas.
    /// </summary>
    /// <param name="seccion">La sección de la que queremos obtener el valor
    /// </param>
    /// <param name="clave">La clave de la que queremos recuperar el valor
    /// </param>
    /// <param name="predeterminado">
    /// El valor predeterminado para cuando no exista.
    /// </param>
    /// <returns>
    /// Un valor de tipo entero con el valor de la sección y clave indicadas
    /// </returns>
    public int GetValue(string seccion, string clave, int predeterminado) 
    {
        return Convert.ToInt32(cfgGetValue(seccion, clave, predeterminado.ToString()));
    }  
    /// <summary>
    /// Obtiene un valor de tipo boolean de la sección y clave indicadas.
    /// </summary>
    /// <param name="seccion">La sección de la que queremos obtener el valor
    /// </param>
    /// <param name="clave">La clave de la que queremos recuperar el valor
    /// </param>
    /// <param name="predeterminado">
    /// El valor predeterminado para cuando no exista.
    /// </param>
    /// <returns>
    /// Un valor de tipo boolean con el valor de la sección y clave indicadas
    /// </returns>
    /// <remarks>
    /// Internamente el valor se guarda con un cero para False y uno para True
    /// </remarks>
    public bool GetValue(string seccion, string clave, bool predeterminado) 
    {
        string def = "0";
        if( predeterminado ) def = "1";
        def = cfgGetValue(seccion, clave, def);
        if( def == "1" )
        {
            return true;
        }
        else
        {
            return false;
        }
    }  

    /// <summary>
    /// Asignar un valor de tipo cadena en la sección y clave indicadas.
    /// </summary>
    /// <param name="seccion">La sección de la que queremos obtener el valor
    /// </param>
    /// <param name="clave">La clave de la que queremos recuperar el valor
    /// </param>
    /// <param name="valor">El valor a asignar</param>
    /// <remarks>
    /// El valor se guardar como un elemento de la sección indicada.
    /// <seealso cref="SetKeyValue" />
    /// </remarks>
    public void SetValue(string seccion, string clave, string valor) 
    {
        cfgSetValue(seccion, clave, valor);
    }  

    /// <summary>
    /// Asignar un valor de tipo entero en la sección y clave indicadas.
    /// </summary>
    /// <param name="seccion">La sección de la que queremos obtener el valor
    /// </param>
    /// <param name="clave">La clave de la que queremos recuperar el valor
    /// </param>
    /// <param name="valor">El valor a asignar</param>
    /// <remarks>
    /// El valor se guardar como un elemento de la sección indicada.
    /// El valor siempre se guarda como un valor de cadena.
    /// <seealso cref="SetKeyValue" />
    /// </remarks>
    public void SetValue(string seccion, string clave, int valor) 
    {
        cfgSetValue(seccion, clave, valor.ToString());
    }  

    /// <summary>
    /// Asignar un valor de tipo boolean en la sección y clave indicadas.
    /// </summary>
    /// <param name="seccion">La sección de la que queremos obtener el valor
    /// </param>
    /// <param name="clave">La clave de la que queremos recuperar el valor
    /// </param>
    /// <param name="valor">El valor a asignar</param>
    /// <remarks>
    /// El valor se guardar como un elemento de la sección indicada.
    /// El valor siempre se guarda como un valor de cadena,
    /// siendo un 1 para True y 0 para False.
    /// <seealso cref="SetKeyValue" />
    /// </remarks>
    public void SetValue(string seccion, string clave, bool valor) 
    {
        if( valor )
        {
            cfgSetValue(seccion, clave, "1");
        }
        else
        {
            cfgSetValue(seccion, clave, "0");
        }
    }  

    /// <summary>
    /// Asigna un valor de tipo cadena en la sección y clave indicadas.
    /// </summary>
    /// <param name="seccion">La sección de la que queremos obtener el valor
    /// </param>
    /// <param name="clave">La clave de la que queremos recuperar el valor
    /// </param>
    /// <param name="valor">El valor a asignar</param>
    /// <remarks>
    /// El valor se guarda como un atributo de la sección indicada.
    /// La clave se guarda con el atributo key y el valor con el atributo value.
    /// <seealso cref="SetValue" />
    /// </remarks>
    public void SetKeyValue(string seccion, string clave, string valor) 
    {
        cfgSetKeyValue(seccion, clave, valor);
    }  

    /// <summary>
    /// Asigna un valor de tipo entero en la sección y clave indicadas.
    /// </summary>
    /// <param name="seccion">La sección de la que queremos obtener el valor
    /// </param>
    /// <param name="clave">La clave de la que queremos recuperar el valor
    /// </param>
    /// <param name="valor">El valor a asignar</param>
    /// <remarks>
    /// El valor se guarda como un atributo de la sección indicada.
    /// La clave se guarda con el atributo key y el valor con el atributo value.
    /// El valor siempre se guarda como un valor de cadena.
    /// <seealso cref="SetValue" />
    /// </remarks>
    public void SetKeyValue(string seccion, string clave, int valor) 
    {
        cfgSetKeyValue(seccion, clave, valor.ToString());
    }  

    /// <summary>
    /// Asigna un valor de tipo boolean en la sección y clave indicadas.
    /// </summary>
    /// <param name="seccion">La sección de la que queremos obtener el valor
    /// </param>
    /// <param name="clave">La clave de la que queremos recuperar el valor
    /// </param>
    /// <param name="valor">El valor a asignar</param>
    /// <remarks>
    /// El valor se guarda como un atributo de la sección indicada.
    /// La clave se guarda con el atributo key y el valor con el atributo value.
    /// El valor siempre se guarda como un valor de cadena,
    /// siendo un 1 para True y 0 para False.
    /// <seealso cref="SetValue" />
    /// </remarks>
    public void SetKeyValue(string seccion, string clave, bool valor) 
    {
        if( valor )
        {
            cfgSetKeyValue(seccion, clave, "1");
        }
        else
        {
            cfgSetKeyValue(seccion, clave, "0");
        }
    }  

    /// <summary>
    /// Elimina la sección indicada, aunque en realidad la deja vacía.
    /// </summary>
    /// <param name="seccion">La sección a eliminar.</param>
    public void RemoveSection(string seccion) 
    {
        XmlNode n;
        n = configXml.SelectSingleNode(configuration + seccion);
        if( n != null )
        {
            n.RemoveAll();
            if( mGuardarAlAsignar )
            {
                this.Save();
            }
        }
    }  

    /// <summary>
    /// Guardar el fichero de configuración.
    /// </summary>
    /// <remarks>
    /// Si no se llama a este método, no se guardará de forma permanente.
    /// Para guardar automáticamente al asignar,
    /// asignar un valor verdadero a la propiedad
    /// <see cref="GuardarAlAsignar">GuardarAlAsignar</see>
    /// </remarks>
    public void Save() 
    {
        configXml.Save(ficConfig);
    }  

    /// <summary>
    /// Lee el fichero de configuración.
    /// </summary>
    /// <remarks>
    /// Si no existe, se crea uno nuevo con los valores predeterminados.
    /// </remarks>
    public void Read() 
    {
        string fic = ficConfig;
        const string revDate = "Thu, 17 Aug 2006 20:20:00 GMT";
        if( File.Exists(fic) )
        {
            configXml.Load(fic);
            // Actualizar los datos de la información de esta clase
            bool b = mGuardarAlAsignar;
            mGuardarAlAsignar = false;
            this.SetValue("configXml_Info", "info", 
			"Generado con ConfigXml para Visual C# 2003");
            this.SetValue("configXml_Info", "revision", revDate);
            this.SetValue("configXml_Info", "formatoUTF8", 
			"El formato de este fichero debe ser UTF-8");
            mGuardarAlAsignar = b;
            this.Save();
        }
        else
        {
            // Crear el XML de configuración con la sección General
            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            sb.Append("<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
            sb.Append("<configuration>");
            // Por si es un fichero appSetting
            sb.Append("<configSections>");
            sb.Append("<section name=\"General\" " + 
                        "type=\"System.Configuration.DictionarySectionHandler\" />");
            sb.Append("</configSections>");
            sb.Append("<General>");
            sb.Append("<!-- Los valores irán dentro del elemento indicado por la clave -->");
            sb.Append("<!-- Aunque también se podrán indicar como pares key / value -->");
            sb.AppendFormat("<add key=\"Revisión\" value=\"{0}\" />", revDate);
            sb.Append("<!-- La clase siempre los añade como un elemento -->");
            sb.Append("<Copyright>©Guillermo 'guille' Som, 2005-2006</Copyright>");
            sb.Append("</General>");
            //
            sb.AppendFormat("<configXml_Info>{0}", "\r\n");
            sb.AppendFormat("<info>Generado con Config para Visual C# 2003" +
			"</info>{0}", "\r\n");
            sb.AppendFormat("<copyright>©Guillermo 'guille' Som, 2005-2006" + 
                "</copyright>{0}", "\r\n");
            sb.AppendFormat("<revision>{0}</revision>{1}", revDate, "\r\n");
            sb.AppendFormat("<formatoUTF8>El formato de este fichero debe ser UTF-8" + 
                            "</formatoUTF8>{0}", "\r\n");
            sb.AppendFormat("</configXml_Info>{0}", "\r\n");
            //
            sb.Append("</configuration>");
            // Asignamos la cadena al objeto
            configXml.LoadXml(sb.ToString());
            //
            // Guardamos el contenido de configXml y creamos el fichero
            configXml.Save(ficConfig);
        }
    }  

    /// <summary>
    /// El nombre del fichero de configuración.
    /// </summary>
    /// <value>
    /// El path completo con el nombre del fichero de configuración.
    /// </value>
    /// <returns>
    /// Una cadena con el fichero de configuración.
    /// </returns>
    /// <remarks>
    /// El nombre del fichero se debe indicar en el constructor.
    /// La dejo como de escritura por si cambiamos el nombre
    /// y usamos el método Save, se guardarán los datos en el nuevo fichero.
    /// </remarks>
    public string FileName 
    {
        get
        {
            return ficConfig;
        }
        set
        {
            // Al asignarlo, NO leemos el contenido del fichero
            ficConfig = value;
            //LeerFile()
        }
    }  

    /// <summary>
    /// Constructor en el que indicamos el nombre del fichero de configuración.
    /// </summary>
    /// <param name="fic">
    /// El fichero a usar para guardar los datos de configuración.
    /// </param>
    /// <remarks>
    /// Si no existe, se creará.
    /// Al usar este constructor, por defecto se guardarán los valores al asignarlos.
    /// </remarks>
    public ConfigXml(string fic) 
    {
        ficConfig = fic;
        // Por defecto se guarda al asignar los valores
        mGuardarAlAsignar = true;
        Read();
    }  

    // Con este constructor podemos decidir si guardamos o no automáticamente
    /// <summary>
    /// Constructor en el que indicamos el nombre del fichero de configuración.
    /// </summary>
    /// <param name="fic">
    /// El fichero a usar para guardar los datos de configuración.
    /// </param>
    /// <param name="guardarAlAsignar">
    /// Un valor verdadero o falso para indicar
    /// si se guardan los datos automáticamente al asignarlos.
    /// </param>
    public ConfigXml(string fic, bool guardarAlAsignar) 
    {
        ficConfig = fic;
        mGuardarAlAsignar = guardarAlAsignar;
        Read();
    }  
    //
    /// <summary>
    /// Devuelve una colección de tipo StringCollection
    /// con las secciones del fichero de configuración.
    /// </summary>
    /// <returns>
    /// Una colección de tipo StringCollection
    /// con las secciones del fichero de configuración.
    /// </returns>
    public StringCollection Secciones() 
    {
        StringCollection d = new StringCollection();
        XmlNode root;
        string s = "configuration";
        root = configXml.SelectSingleNode(s);
        if( root != null )
        {
            foreach(XmlNode n in root.ChildNodes)
            {
                d.Add(n.Name);
            }
        }
        return d;
    }  

    /// <summary>
    /// Devuelve una colección de tipo StringDictionary
    /// con las claves y valores de la sección indicada.
    /// </summary>
    /// <param name="seccion">
    /// La sección de la que queremos obtener las claves y valores.
    /// </param>
    /// <returns>
    /// Una colección de tipo StringDictionary con las claves y valores.
    /// </returns>
    public StringDictionary Claves(string seccion) 
    {
        StringDictionary d = new StringDictionary();
        XmlNode root;
        seccion = seccion.Replace(" ", "_");
        root = configXml.SelectSingleNode(configuration + seccion);
        if( root != null )
        {
            foreach(XmlNode n in root.ChildNodes)
            {
                if( d.ContainsKey(n.Name) == false )
                {
                    d.Add(n.Name, n.InnerText);
                }
            }
        }
        return d;
    }  
    //
    //----------------------------------------------------------------------
    // Los métodos privados
    //----------------------------------------------------------------------
    //
    // El método interno para guardar los valores
    // Este método siempre guardará en el formato:
    // <seccion><clave>valor</clave></seccion>
    private void cfgSetValue(string seccion, string clave, string valor) 
    {
        //
        XmlNode n;
        //
        // Filtrar los caracteres no válidos
        // en principio solo comprobamos el espacio
        seccion = seccion.Replace(" ", "_");
        clave = clave.Replace(" ", "_");

        // Se comprueba si es un elemento de la sección:
        //   <seccion><clave>valor</clave></seccion>
        n = configXml.SelectSingleNode(configuration + seccion + "/" + clave);
        if( n != null )
        {
            n.InnerText = valor;
        }
        else
        {
            XmlNode root;
            XmlElement elem;
            root = configXml.SelectSingleNode(configuration + seccion);
            if( root == null )
            {
                // Si no existe el elemento principal,
                // lo añadimos a <configuration>
                elem = configXml.CreateElement(seccion);
                configXml.DocumentElement.AppendChild(elem);
                root = configXml.SelectSingleNode(configuration + seccion);
            }
            if( root != null )
            {
                // Crear el elemento
                elem = configXml.CreateElement(clave);
                elem.InnerText = valor;
                // Añadirlo al nodo indicado
                root.AppendChild(elem);
            }
        }
        //
        if( mGuardarAlAsignar )
        {
            this.Save();
        }
    }  

    // Asigna un atributo a una sección
    // Por ejemplo: <Seccion clave=valor>...</Seccion>
    // También se usará para el formato de appSettings:
    // <add key=clave value=valor />
    // Aunque en este caso, debe existir el elemento a asignar.
    private void cfgSetKeyValue(string seccion, string clave, string valor) 
    {
        //
        XmlNode n;
        //
        // Filtrar los caracteres no válidos
        // en principio solo comprobamos el espacio
        seccion = seccion.Replace(" ", "_");
        clave = clave.Replace(" ", "_");
        n = configXml.SelectSingleNode(configuration + seccion + "/add[@key=\"" + clave + "\"]");
        if( n != null )
        {
            n.Attributes["value"].InnerText = valor;
        }
        else
        {
            XmlNode root;
            XmlElement elem;
            root = configXml.SelectSingleNode(configuration + seccion);
            if( root == null )
            {
                // Si no existe el elemento principal,
                // lo añadimos a <configuration>
                elem = configXml.CreateElement(seccion);
                configXml.DocumentElement.AppendChild(elem);
                root = configXml.SelectSingleNode(configuration + seccion);
            }
            if( root != null )
            {
                XmlAttribute a;
                a = ((XmlAttribute)configXml.CreateNode(XmlNodeType.Attribute, clave, null));
                a.InnerText = valor;
                root.Attributes.Append(a);
            }
        }
        if( mGuardarAlAsignar )
        {
            this.Save();
        }
    }  

    // Devolver el valor de la clave indicada
    private string cfgGetValue(string seccion, string clave, string valor) 
    {
        //
        XmlNode n;
        //
        // Filtrar los caracteres no válidos
        // en principio solo comprobamos el espacio
        seccion = seccion.Replace(" ", "_");
        clave = clave.Replace(" ", "_");
        // Primero comprobar si están el formato de appSettings:
        // <add key = clave value = valor />
        n = configXml.SelectSingleNode(configuration + seccion + "/add[@key=\"" + clave + "\"]");
        if( n != null )
        {
            return n.Attributes["value"].InnerText;
        }
        //
        // Después se comprueba si está en el formato <Seccion clave = valor>
        n = configXml.SelectSingleNode(configuration + seccion);
        if( n != null )
        {
            XmlAttribute a = n.Attributes[clave];
            if( a != null )
            {
                return a.InnerText;
            }
        }
        //
        // Por último se comprueba si es un elemento de seccion:
        //   <seccion><clave>valor</clave></seccion>
        n = configXml.SelectSingleNode(configuration + seccion + "/" + clave);
        if( n != null )
        {
            return n.InnerText;
        }
        //
        // Si no existe, se devuelve el valor predeterminado
        return valor;
    }  
}

}

 


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

System.Collections.Specialiced
System.Xml
System.IO

 


Código de ejemplo (ZIP):

 

En el ZIP se incluye tanto el código fuente para Visual Basic como para C#.

Fichero con el código de ejemplo: configXml_2003.zip - 104 KB

(MD5 checksum: AD8B0C66CA5FF60332F42CECCB8FC601)

 


Ir al índice principal de el Guille