从 C# 读取 XML 配置数据文件
本文关键字:置数据 文件 配置 XML 读取 | 更新日期: 2023-09-27 18:32:11
我有一个名为config.xml的独立XML文件,它与我的应用程序一起使用,基本上包含两个部分:
1) 全局设置2) 服务器列表,包括设置
基本上,全局设置将包含我的程序将用于每个服务器列表的数据库用户名和数据库密码。
服务器列表条目包含服务器列表、一些文件名以及 Databse 用户名和数据库密码。 这里唯一重要的是,如果我在服务器列表中指定用户名/密码,那么它将使用它而不是全局数据库用户名和密码。 或者,换句话说,如果数据库用户名和密码未在 servewr 列表条目中定义,它将使用全局数据库用户名 pasword。
我的程序基本上循环访问和 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>
您需要做的是创建一组类,每个类代表每组节点。 如果您想使用这些扩展,它们将帮助您处理空节点和默认值:
在全局中读取和写入输出文件夹:
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()
采用默认值,因此它始终具有值。
一旦具有类区分,向文件添加新值就更简单了,因为您只需将另一个属性写入类,并让它返回默认值(如果它不存在)。