2

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

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

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

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

我的程序基本上循环遍历 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>
4

1 回答 1

3

您需要做的是创建一组类,每个类代表每组节点。如果您想使用这些扩展,它们将帮助您处理空节点和默认值:

在全局中读取和写入 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()采用默认值,因此它总是有一个值。

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

于 2012-04-26T00:04:37.163 回答