Feedback

C# - XML-Programmkonfiguration / -Steuerung

Veröffentlicht von am 11.12.2006
(4 Bewertungen)
Mit Hilfe dieser Klasse lassen sich sehr einfach Objekte mit öffentlichen Eigenschaften per XML Datei setzen.
Dies geschieht über Reflection, und kann daher auf jedes Objekt angewandt werden.

Bisher können 4 verschiedene Arten von Eigenschaften gesetzt werden: Color, Font, Enum und String. Durch ein Switch-Case Modell und entsprechende String-Value Konvertierungsmethoden kann dies aber beliebig erweitert werden.

Highlights:
- Es können geschachtelte Properties mittels Punkt getrennt (so wie man es im Code auch verwenden würde) im XML Konfigurationsfile benutzt werden.
- Dies kann zur Runtime angewendet werden
- Es benötigt nur eine Zeile Code bei der Anwendung

Viel Spaß damit, ich freue mich auf Bewertungen,
_ntr_
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Drawing;
using System.Reflection;

namespace Snippets_Wettbewerb.XMLConfigReader
{
    /// <summary>
    /// This class reads xml configuration file and will try to translate all xml nodes
    /// into configuration strings. The configuration strings will be used to set public
    /// properties from specified object via runtime and reflection. There are several
    /// ways to define kind of property values (e.g. color-definition).
    /// </summary>
    /// <example>
    /// Following xml configuration:
    /// <?xml version="1.0" encoding="utf-8" ?>
    /// <Config>
    ///   <TestString>This is a string.</TestString>
    ///   <TestColor1 type="rgb">125, 125, 125</TestColor1>
    ///   <TestColor2 type="colorname">White</TestColor2>
    ///   <TestFont type="font">Microsoft Sans Sarif, 10, bold</TestFont>
    /// </Config>
    /// Can be used with objects containing "TestString", "TestColor1", "TestColor2" and "TestFont"
    /// properties with following instruction:
    /// <code>XMLConfigReader.XmlConfigReader.ReadAllConfig("config.xml", this);</code>
    /// </example>
    public class XmlConfigReader
    {
        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="T:BVXmlConfigReader"/> class.
        /// </summary>
        private XmlConfigReader()
        {
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Method which gets all property nodes in xml config file.
        /// </summary>
        /// <param name="p_sXmlFilenamePath">Name and path of xml config file.</param>
        /// <param name="p_sXmlRoot">Xml path root, from where to search properties.</param>
        /// <returns>XmlNodeList of all properties, or null if xml file/rootnode was not found.</returns>
        private static XmlNodeList GetXmlNodes(string p_sXmlFilenamePath, string p_sXmlRoot)
        {
            // check whether xml file exists
            if (System.IO.File.Exists(p_sXmlFilenamePath))
            {
                // local variable
                string sXmlPathRoot = String.Empty;

                // load xml file
                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.Load(p_sXmlFilenamePath);

                // create XPath expression
                sXmlPathRoot = CreateXPathExpression(p_sXmlRoot, "*");

                // return selected nodes
                return xmlDoc.SelectNodes(sXmlPathRoot);
            }

            return null;
        }

        /// <summary>
        /// Method which get single xml node from xml config file.
        /// </summary>
        /// <param name="p_sXmlFilenamePath">Name and path of xml config file.</param>
        /// <param name="p_sXmlRoot">Xml path root, from where to search properties.</param>
        /// <param name="p_sNodename">Xml node name which should be received.</param>
        /// <returns>XmlNode of requested property or null if node was not found.</returns>
        private static XmlNode GetSingleXmlNode(string p_sXmlFilenamePath, string p_sXmlRoot, string p_sNodename)
        {
            // check whether file exists
            if (System.IO.File.Exists(p_sXmlFilenamePath))
            {
                // load xml file
                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.Load(p_sXmlFilenamePath);

                // create XPath expression
                string sXmlPathRoot = CreateXPathExpression(p_sXmlRoot, p_sNodename);

                // return selected node
                return xmlDoc.DocumentElement.SelectSingleNode(sXmlPathRoot);
            }

            return null;
        }

        /// <summary>
        /// Method which creates xml path expression to find all nodes defined with specified
        /// root node (which don't have to be same like document element node).
        /// </summary>
        /// <param name="p_sXmlRoot">Name of inner root node. May be same like document element node.</param>
        /// <param name="p_sNodename">Define one specific node name where to search.</param>
        /// <returns>XPath Expression pointing to specific root/named node.</returns>
        private static string CreateXPathExpression(string p_sXmlRoot, string p_sNodename)
        {
            // local variable
            string sXmlPathRoot = String.Empty;

            // concat XmlRoot (to search) with XmlPathRoot
            if (p_sXmlRoot != String.Empty)
            {
                sXmlPathRoot = String.Concat(
                    p_sXmlRoot,
                    "/",
                    p_sNodename);
            }
            else
            {
                sXmlPathRoot = String.Concat(
                    "./*/",
                    p_sNodename);
            }
            return sXmlPathRoot;
        }

        /// <summary>
        /// Method which tries to set property from xml node.
        /// </summary>
        /// <param name="node">The xml node.</param>
        private static bool SetPropertyFromXmlNode(XmlNode node, object p_oConcreteInstance)
        {
            // local variables
            string sAttributeType = "default";

            // check if node or object is null
            if ((node == null) || (p_oConcreteInstance == null))
            {
                return false;
            }

            // check if property with sub property is given
            string[] sSubProperties = node.Name.Split('.');

            // detect if given property exists
            PropertyInfo propInfo = null;
            Type currPropertyType = p_oConcreteInstance.GetType();

            // go straight forward with nested types until last property should get
            for (int i = 0; i < sSubProperties.Length; i++)
            {
                // name of current property
                string sCurrentProperty = sSubProperties[i];

                // if last property detected, get it PropertyInfo object, otherwise get nested type
                if (i == sSubProperties.Length-1)
                {
                    propInfo = currPropertyType.GetProperty(sSubProperties[i]);
                }
                else
                {
                    PropertyInfo pi = currPropertyType.GetProperty(sSubProperties[i]);
                    currPropertyType = pi.GetValue(p_oConcreteInstance, null).GetType();

                    // create new instane of current property value
                    try
                    {
                        object obj = pi.GetValue(p_oConcreteInstance, null);
                        p_oConcreteInstance = obj;
                    }
                    catch
                    {
                        break;
                    }

                    // check if nested type was found, otherwise break
                    if (currPropertyType == null)
                    {
                        break;
                    }
                }
            }

            // check if property info was found
            if (propInfo == null)
            {
                return false;
            }
            if (!propInfo.CanWrite)
            {
                return false;
            }

            // check if node attribute "type" is specified
            XmlAttribute xmlAttr = (XmlAttribute)node.Attributes.GetNamedItem("type");
            if (xmlAttr != null)
            {
                sAttributeType = xmlAttr.Value.ToLower();
            }

            try
            {
                // switch for all different types
                switch (sAttributeType)
                {
                    case "rgb":
                        Color color = ConvertRGBString(node.InnerText);
                        if (color != Color.Empty)
                        {
                            propInfo.SetValue(p_oConcreteInstance, color, null);
                        }
                        break;
                    case "colorname":
                        color = Color.FromName(node.InnerText);
                        if (!color.Equals(Color.FromArgb(0)))
                        {
                            propInfo.SetValue(p_oConcreteInstance, color, null);
                        }
                        break;
                    case "bool":
                        string nodeVal = node.InnerText;
                        bool convertedVal = false;
                        if (nodeVal.ToLower() == "true")
                        {
                            convertedVal = true;
                        }
                        propInfo.SetValue(p_oConcreteInstance, convertedVal, null);
                        break;
                    case "font":
                        Font fontToSet = ConvertFontString(node.InnerText);
                        propInfo.SetValue(p_oConcreteInstance, fontToSet, null);
                        break;
                    case "enum":
                        object enumToSet = ConvertEnumString(node.InnerText,
                            propInfo.GetValue(p_oConcreteInstance, null));
                        propInfo.SetValue(p_oConcreteInstance, enumToSet, null);
                        break;
                    default:
                        propInfo.SetValue(p_oConcreteInstance, node.InnerText, null);
                        break;
                }
            }
            catch (Exception ex)
            {
                // TODO: Decide what is necessary: Exception or log entry...
                return false;
            }

            return true;
        }

        #endregion

        #region Converting routines

        /// <summary>
        /// Converts the enum string.
        /// </summary>
        /// <param name="p_sEnumString">The EnumString.</param>
        /// <param name="p_oCurrentEnumValue">The current enum value object to detect which enum is necessary.</param>
        /// <returns>Parsed enum value.</returns>
        private static object ConvertEnumString(string p_sEnumString, object p_oCurrentEnumValue)
        {
            object retValue = Enum.Parse(p_oCurrentEnumValue.GetType(), p_sEnumString);
            return retValue;
        }

        /// <summary>
        /// Converts the font string.
        /// </summary>
        /// <param name="p_sFontString">The Font string coming from xml file.</param>
        /// <returns>Converted Font object, or null if conversion fails.</returns>
        private static Font ConvertFontString(string p_sFontString)
        {
            // local variables
            float fFontSize = 0;
            FontStyle fontStyle;
            string[] fontParams = p_sFontString.Split(',');

            // check count of input parameters
            if (fontParams.Length < 3)
            {
                return null;
            }

            // try to parse font size
            if (!Single.TryParse(fontParams[1], out fFontSize))
            {
                fFontSize = 10f;
            }

            // try to parse font style
            try
            {
                fontStyle = (FontStyle)Enum.Parse(typeof(FontStyle), fontParams[2], true);
            }
            catch
            {
                fontStyle = FontStyle.Regular;
            }

            return new Font(fontParams[0], fFontSize, fontStyle);
        }

        /// <summary>
        /// Converts the RGB string to Color object.
        /// </summary>
        /// <param name="p">String contains comma seperated rgb values.</param>
        /// <returns>Color from RGB values.</returns>
        private static Color ConvertRGBString(string p_sRGBValues)
        {
            // local variables
            int iColorR = 300; // initial illegal range
            int iColorG = 300; // initial illegal range
            int iColorB = 300; // initial illegal range
            string[] rgbValues = p_sRGBValues.Split(',');

            // check if 3 rgb values exists
            if (rgbValues.Length != 3)
            {
                return Color.Empty;
            }

            // try to parse 3 rgb values to integer
            Int32.TryParse(rgbValues[0], out iColorR);
            Int32.TryParse(rgbValues[1], out iColorG);
            Int32.TryParse(rgbValues[2], out iColorB);

            // check rgb range
            if ((iColorR < 0) || (iColorR > 255))
            {
                return Color.Empty;
            }
            if ((iColorG < 0) || (iColorG > 255))
            {
                return Color.Empty;
            }
            if ((iColorB < 0) || (iColorB > 255))
            {
                return Color.Empty;
            }

            // return RGB color
            return Color.FromArgb(iColorR, iColorG, iColorB);
        }

        #endregion

        #region Public / Protected Methods

        /// <summary>
        /// This method reads from xml configuration file one property and tries to use it in concrete
        /// object instance.
        /// </summary>
        /// <param name="p_sXmlFilePath">Filepath to xml config file.</param>
        /// <param name="p_sProperty">Name of property (XmlNode name), which to read.</param>
        /// <param name="p_oConcreteInstance">Concrete instance for which property should be set.</param>
        /// <returns>
        /// True, if property setting was successfully, otherwise False.
        /// </returns>
        public static bool ReadConfig(string p_sXmlFilePath, string p_sProperty, object p_oConcreteInstance)
        {
            XmlNode node = GetSingleXmlNode(p_sXmlFilePath, String.Empty, p_sProperty);
            return SetPropertyFromXmlNode(node, p_oConcreteInstance);
        }

        /// <summary>
        /// This method reads from xml configuration file all properties and tries to use them in concrete
        /// object instance.
        /// </summary>
        /// <param name="p_sXmlFilePath">Filepath to xml config file.</param>
        /// <param name="p_oConcreteInstance">Concrete instance for which properties should be set.</param>
        /// <returns>
        /// True, if properties setting was successfully, otherwise False.
        /// </returns>
        public static bool ReadAllConfig(string p_sXmlFilePath, object p_oConcreteInstance)
        {
            // local variable
            bool retValue = true;

            XmlNodeList nodeList = GetXmlNodes(p_sXmlFilePath, String.Empty);

            if (nodeList == null)
            {
                return false;
            }

            foreach (XmlNode node in nodeList)
            {
                if(!SetPropertyFromXmlNode(node, p_oConcreteInstance))
                {
                    retValue = false;
                }
            }

            return retValue;
        }

        #endregion
    }
}

Abgelegt unter XML, Konfiguration, Reflection.

4 Kommentare zum Snippet

Thomas Westrupp schrieb am 11.12.2006:
Top Snippet, wenn auch ein wenig umfangreich, aber doch absolut simpel in der Einsetzung!
_ntr_ schrieb am 11.12.2006:
Vielen Dank,
auch wenn mir unerklärlich ist, wie man mit 1 Bewertung eine Kommazahl als Wertung erhält ;-)

Zieht man hier alle Kommentare ab, dann ist das Ganze gar nicht mehr so umfangreich ;-)
Jan Welker schrieb am 11.12.2006:
Wie sich die Bewertung zusammensetzt kann man hier <a href="http://dotnet-snippets.de/dns/TopTen.aspx">http://dotnet-snippets.de/dns/TopTen.aspx</a> (unten) nachlesen.
_ntr_ schrieb am 13.12.2006:
Hallo,
ich sehe, dass mein Snippet bewertet worden ist, aber ein Kommentar fehlt. Ich kann zwar nicht verlangen, dass ein Kommentar abgegeben wird, aber ich kann darum bitten. Kommentare sind mir deshalb wichtiger als eine Bewertung.
Also bitte bei Verwendung meines Snippets auch einen Kommentar abgeben, danke!
_ntr_
 

Logge dich ein, um hier zu kommentieren!