从C#读取XML配置数据文件

时间:2012-04-25 20:36:00

标签: c# xml

我有一个名为config.xml的独立XML文件,它与我的应用程序一起包含两个部分:

1)全局设置 2)服务器列表,包括设置

基本上,全局设置将包含我的程序将用于每个服务器列表的数据库用户名和数据库密码。

服务器列表条目包含服务器列表以及一些文件名,以及数据库用户名和数据库密码。这里唯一重要的是,如果我在服务器列表中指定用户名/密码,那么它将使用此而不是全局数据库用户名和密码。或者,换句话说,如果未在servewr列表条目中定义数据库用户名和密码,则它将使用全局数据库用户名密码。

我的程序基本上循环遍历xml配置文件,并针对每个DB2服务器执行一些数据库查询,并处理信息并创建报告。它今天有效,但我确实有一些问题......

1)每次我向XML配置文件添加一个新元素时,我必须为我创建的每个节点添加它,否则我会得到XML解析错误。

2)我想提供配置XML文件,而不是将所有内容放在同一个节点中,并包含空元素。

示例XML如下:

<?xml version="1.0" encoding="utf-8" ?>
<Config>
    <Global>
    <OutputFolder>C:\DATA\Configs\DB2\</OutputFolder>
    <DBUser>DB2ADMIN</DBUser>
    <DBPassword>%SecretPassword%</DBPassword>
    <FTPFiles>false</FTPFiles>
    <FTPTcpIp>127.0.0.1</FTPTcpIp>
    <FTPUser>FTPLogin1</FTPUser>
    <FTPPassword>P@ssw0rd</FTPPassword>
    <FTPRemoteFolder>/configs</FTPRemoteFolder>
    </Global>
    <Servers>
        <Server enabled="true">
        <Category>Report1</Category>
        <TcpIp>192.168.26.107</TcpIp>
        <Database>SampleData</Database>
        <User></User>
        <Password></Password>
        <Report1FileNameList>List1.txt</Report1FileNameList>
        <Report1FileNameRoutes>Routes1.txt</Report1FileNameRoutes>
        <Report1FileNameRouteTimeouts>Timeouts1.txt</Report1FileNameRouteTimeouts>
        <Report1FileNameEndpoints></Report1FileNameEndpoints>
        <Report2FilenameServers></Report2FilenameServers>
        <Report2FilenameRoutingGroup></Report2FilenameRoutingGroup>
     </Server>
     <Server enabled="true">
         <Category>Report1</Category>
         <TcpIp>192.168.26.107</TcpIp>
         <Database>SampleDataB</Database>
         <User></User>
         <Password></Password>
         <Report1FileNameList>List1.txt</Report1FileNameList>
         <Report1FileNameRoutes>Routes1.txt</Report1FileNameRoutes>
         <Report1FileNameRouteTimeouts>Timeouts1.txt</Report1FileNameRouteTimeouts>
         <Report1FileNameEndpoints></Report1FileNameEndpoints>
         <Report2FilenameServers></Report2FilenameServers>
         <Report2FilenameRoutingGroup></Report2FilenameRoutingGroup>
      </Server>
      <Server enabled="true">
          <Category>Report2</Category>
          <TcpIp>192.168.26.107</TcpIp>
          <Database>SampleDataE</Database>
          <User></User>
          <Password></Password>
          <Report1FileNameList></Report1FileNameList>
          <Report1FileNameRoutes></Report1FileNameRoutes>
          <Report1FileNameRouteTimeouts></Report1FileNameRouteTimeouts>
          <Report1FileNameEndpoints>Endpoints2.txt</Report1FileNameEndpoints>
          <Report2FilenameServers>Servers2.txt</Report2FilenameServers>
          <Report2FilenameRoutingGroup>Groups2.txt</Report2FilenameRoutingGroup>
      </Server>
      <Server enabled="true">
          <Category>Report2</Category>
          <TcpIp>192.168.26.108</TcpIp>
          <Database>SampleDatabase1_D</Database>
          <User></User>
          <Password></Password>
          <Report1FileNameList></Report1FileNameList>
          <Report1FileNameRoutes></Report1FileNameRoutes>
          <Report1FileNameRouteTimeouts></Report1FileNameRouteTimeouts>
          <Report1FileNameEndpoints>Endpoints2.txt</Report1FileNameEndpoints>
          <Report2FilenameServers>Servers2.txt</Report2FilenameServers>
          <Report2FilenameRoutingGroup>Groups1.txt</Report2FilenameRoutingGroup>
       </Server>
    </Servers>

示例代码如下:

        // load XML file
        try
        {
            // Config/Global
            System.Xml.XPath.XPathDocument doc = new System.Xml.XPath.XPathDocument(@"config.xml");

            foreach (System.Xml.XPath.XPathNavigator child in doc.CreateNavigator().Select("Config/Global"))
            {
                xml_global_outputFolder = child.SelectSingleNode("OutputFolder").Value;
                xml_global_DBuser = child.SelectSingleNode("DBUser").Value;
                xml_global_DBpassword = child.SelectSingleNode("DBPassword").Value;
                xml_global_FTPFiles = bool.Parse(child.SelectSingleNode("FTPFiles").Value);
                xml_global_FTPTcpIp = child.SelectSingleNode("FTPTcpIp").Value;
                xml_global_FTPUser = child.SelectSingleNode("FTPUser").Value;
                xml_global_FTPPassword = child.SelectSingleNode("FTPPassword").Value;
                xml_global_FTPRemoteFolder = child.SelectSingleNode("FTPRemoteFolder").Value;
            }

            // Config/Servers
            //System.Xml.XPath.XPathDocument doc = new System.Xml.XPath.XPathDocument(@"config.xml");
            foreach (System.Xml.XPath.XPathNavigator child in doc.CreateNavigator().Select("Config/Servers/*"))
            {

                //string xml_enabled = child.GetAttribute("Enabled", "");
                string xml_category = child.SelectSingleNode("Category").Value;
                string xml_tcpip = child.SelectSingleNode("TcpIp").Value;
                string xml_database = child.SelectSingleNode("Database").Value;
                string xml_user = child.SelectSingleNode("User").Value;
                string xml_password = child.SelectSingleNode("Password").Value;

                Console.WriteLine("Connecting to {0} using database {1} for {2} information...", xml_tcpip, xml_database, xml_category);

                // if node user value is empty, use global
                if (xml_user == string.Empty)
                {
                    DB2_user = xml_global_DBuser;
                }
                else
                {
                    DB2_user = xml_user;
                }

                // if node password value is empty, use global
                if (xml_password == string.Empty)
                {
                    DB2_password = xml_global_DBpassword;
                }
                else
                {
                    DB2_password = xml_password;
                }

                string txtFilename = string.Empty;
                string csvFilename = string.Empty;

                switch (xml_category.ToUpper())
                {
                    case "SAMPLE":
                        txtFilename = Path.Combine(xml_global_outputFolder, @"EMPLOYEE.csv");
                        csvFilename = Path.Combine(xml_global_outputFolder, Path.GetFileNameWithoutExtension(@"EMPLOYEE.csv"));
                        ExecuteQuery(xml_category, "SAMPLE", xml_tcpip, DB2_user, DB2_password, xml_database, csvFilename, txtFilename, option_debug);
                        break;
                }
                Console.WriteLine("");

            }
        }
        catch (Exception e)
        {
            Console.WriteLine("Exception: {0}", e.Message);
            Environment.Exit(1);
        }

        Environment.Exit(0);
    }

我想理想情况下,我想创建特定于节点的配置,如果元素为空,则代码应该能够处理缺少的元素或空元素。也许使用该类别的属性?类似的东西:

   <Config>
  <Global>
     <OutputFolder></OutputFolder>
     <DBUser></DBUser>
     <DBPassword><DBPassword>
  </Global>
  <Servers category="Report1">
     <Server>
        <TcpIP>whatever</TcpIP>
        <User>whatever></User>
        <Password>whatever></Password>
     </Server>
     <Server>
        <TcpIP>whatever</TcpIP>
        <User>whatever></User>
        <Password>whatever></Password>
     </Server>
     <Server>
        <TcpIP>whatever</TcpIP>
        <User>whatever></User>
        <Password>whatever></Password>
     </Server>
 </Server>
  <Servers category="Report2">
     <Server>
        <TcpIP>whatever</TcpIP>
        <User>whatever></User>
        <Password>whatever></Password>
     </Server>
     <Server>
        <TcpIP>whatever</TcpIP>
        <User>whatever></User>
        <Password>whatever></Password>
     </Server>
     <Server>
        <TcpIP>whatever</TcpIP>
        <User>whatever></User>
        <Password>whatever></Password>
     </Server>
 </Server>
  <Servers category="AccessList">
     <Server>
        <TcpIP>whatever</TcpIP>
        <Database>whatever></Database>
        <Active>whatever</Active>
     </Server>
     <Server>
        <TcpIP>whatever</TcpIP>
        <Database>whatever></Database>
        <Active>whatever</Active>
     </Server>
     <Server>
        <TcpIP>whatever</TcpIP>
        <Database>whatever></Database>
        <Active>whatever</Active>
     </Server>
 </Server>
</Config>

1 个答案:

答案 0 :(得分:3)

您需要做的是创建一组类,每个类代表每组节点。如果您想使用these extensions,他们会帮助您处理空节点和默认值:

在Global中读取和写入OutputFolder:

DirectoryInfo outputFolder = ConfigFile.Read.Global.OutputFolder;
ConfigFile.Write(file => file.Global.OutputFolder = outputFolder);

课程:

public class ConfigFile : IDisposable
{
    internal XElement self;
    string file = "path to a file";

    public ConfigFile()
    {
        if(File.Exists(file))
            self = XElement.Load(file);
        else
            self = new XElement("Config");
    }

    public void Dispose() { self.Save(file); }

    public static ConfigFile Read { get { return new ConfigFile(); } }

    public static void Write(Action<ConfigFile> action)
    {
        using(ConfigFile file = new ConfigFile())
            action(file);
    }

    public Global Global
    { get { return _Global ?? (_Global = new Global(self.GetElement("Global"))); } }
    Global _Global;

    public Servers Servers
    { get { return _Servers ?? (_Servers = new Servers(self.GetElement("Servers"))); } }
    Servers _Servers

    public class Global
    {
        internal XElement self;
        public Global(XElement self) { this.self = self; }

        public DirectoryInfo OutputFolder
        {
            get { return self.Get<DirectoryInfo>("OutputFolder", null); }
            set { self.Set("OutputFolder", value, false); }
        }
    }

    public class Servers
    {
        internal XElement self;
        public Servers(XElement self) { this.self = self; }

        public void Add(Server server)
        {
            self.Add(server.self);
        }

        public string Category
        {
            get { return self.Get("category", string.Empty); }
            set { self.Set("category", value, true); }
        }

        public Server[] Items
        { get { return self.GetEnumerable("Server", x => new Server(x)).ToArray(); } }

        public class Server
        {
            internal XElement self;

            public Server() { self = new XElement("Server"); }

            public Server(XElement self) { this.self = self; }

            public bool Active
            {
                get { return self.Get("Active", false); }
                set { self.Set("Active", value, true); }
            }
        }
    }
}

GetElement()优于Element(),因为它处理元素节点不存在的情况。 Get()采用默认值,因此它始终具有值。

一旦你有类别区别,为文件添加一个新值就更简单了,因为你可以简单地将另一个属性写入一个类,如果它不存在则让它返回一个默认值。