由于xml文件的易读特性,使得它非常适合作为程序配置文件。和ini文件相比,xml文件可以实现列表等复杂参数配置,灵活性比较大。

使用C#读取xml文件,首先要加载xml文件获取XmlDocument对象,然后通过该对象获取XmlNode类型的根节点,之后再对根节点获取相应子节点的属性或值。写入xml文件时,获取对应的节点后设置其属性或者值即可。

本文对C#操作xml文件进行了封装处理,方便代码复用。

1、xml文件内容

<?xml version="1.0" encoding="utf-8"?>
<AppSettings>
  <!-- 调试模式:0-否,1-是 -->
  <DebugMode>0</DebugMode>
  <!-- FTP服务器参数 -->
  <FTP IP="127.0.0.1" Port="21" UserName="user" Password="user"/>
  <!-- 学生信息:Grade-年级,Class-班级 -->
  <Students Grade="1" Class="7">
    <!-- 学生:Name-姓名,Age-年龄 -->
    <Student Name="Name1" Age="6"/>
    <Student Name="Name2" Age="7"/>
    <Student Name="Name3" Age="7"/>
    <Student Name="Name4" Age="6"/>
  </Students>
</AppSettings>

2、XmlHelper.cs

public static class XmlHelper
{
    public static bool GetNodeInnerInt(XmlNode node, string xpath, out int value)
    {
        var text = node?.SelectSingleNode(xpath)?.InnerText.Trim();
        return int.TryParse(text, out value);
    }

    public static bool GetNodeInnerStr(XmlNode node, string xpath, out string value)
    {
        value = node?.SelectSingleNode(xpath)?.InnerText.Trim();
        return !string.IsNullOrEmpty(value);
    }

    public static void SetNodeInnerValue(XmlNode node, string xpath, string text)
    {
        var item = node?.SelectSingleNode(xpath);
        if (item != null)
        {
            item.InnerText = text;
        }
    }

    public static bool GetNodeAttributeInt(XmlNode node, string xpath, out int value)
    {
        var text = node?.Attributes?.GetNamedItem(xpath)?.Value.Trim();
        return int.TryParse(text, out value);
    }

    public static bool GetNodeAttributeStr(XmlNode node, string xpath, out string value)
    {
        value = node?.Attributes?.GetNamedItem(xpath)?.Value.Trim();
        return !string.IsNullOrEmpty(value);
    }

    public static void SetNodeAttributeValue(XmlNode node, string xpath, string text)
    {
        var item = node?.Attributes?.GetNamedItem(xpath);
        if (item != null)
        {
            item.Value = text;
        }
    }
}

3、XmlConfigManager.cs

internal class StudentInfo
{
    public string Name { get; set; }
    public int Age { get; set; }

    public StudentInfo(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

internal class ClassInfo
{
    public int Grade { get; set; }
    public int Class { get; set; }

    public List<StudentInfo> StudentInfoList { get; }

    public ClassInfo()
    {
        StudentInfoList = new List<StudentInfo>();
    }
}

internal sealed class XmlConfigManager
{
    public bool DebugMode { get; private set; } //调试模式
    public ClassInfo ClassInfo { get; } //班级信息

    #region 数据库参数

    public string DbIp { get; private set; } //数据库IP
    public int DbPort { get; private set; } = 1433; //数据库端口
    public string DbCatalog { get; private set; } //数据库名称
    public string DbUser { get; private set; } //数据库用户名
    public string DbPassword { get; private set; } //数据库密码

    public string ConnectionString =>
        $@"data source={DbIp},{DbPort};initial catalog={DbCatalog};persist security info=True;user id={DbUser};password={DbPassword}";

    #endregion

    private string _configName;
    private static readonly ILog Logger = LogManager.GetLogger(nameof(XmlConfigManager));

    /// <summary>
    /// 初始化配置
    /// </summary>
    public void LoadConfig(string configName)
    {
        _configName = configName;

        //Get Config file
        var info = new DirectoryInfo(Assembly.GetExecutingAssembly().Location);
        var xmlFilePath = Path.Combine(info.Parent?.FullName ?? string.Empty, _configName);

        //从配置文件读取数据
        var xmlDoc = new XmlDocument();
        try
        {
            xmlDoc.Load(xmlFilePath);
        }
        catch (Exception e)
        {
            Logger.Error($@"加载配置文件[{configName}]失败:{e.Message}.");
            return;
        }

        var root = xmlDoc.SelectSingleNode("/AppSettings");
        LoadDebugModeConfig(root); //加载调试模式
        LoadDatabaseConfig(root); //加载数据库参数
        LoadClassInfoConfig(root); //加载班级信息
    }

    #region 获取参数

    private void LoadDebugModeConfig(XmlNode root)
    {
        try
        {
            var node = root?.SelectSingleNode("Common");
            if (XmlHelper.GetNodeInnerStr(node, "DebugMode", out var str))
            {
                DebugMode = str == "1";
            }
        }
        catch (Exception e)
        {
            Logger.Error($@"加载调试模式异常:{e.Message}.");
        }
    }

    private void LoadDatabaseConfig(XmlNode root)
    {
        try
        {
            var node = root?.SelectSingleNode("Database");
            if (XmlHelper.GetNodeAttributeStr(node, "Ip", out var ip))
            {
                DbIp = ip;
            }

            if (XmlHelper.GetNodeAttributeInt(node, "Port", out var port))
            {
                DbPort = port;
            }

            if (XmlHelper.GetNodeAttributeStr(node, "Catalog", out var catalog))
            {
                DbCatalog = catalog;
            }

            if (XmlHelper.GetNodeAttributeStr(node, "User", out var user))
            {
                DbUser = user;
            }

            if (XmlHelper.GetNodeAttributeStr(node, "Password", out var password))
            {
                DbPassword = password;
            }
        }
        catch (Exception e)
        {
            Logger.Error($@"加载数据库参数异常:{e.Message}.");
        }
    }

    private void LoadClassInfoConfig(XmlNode root)
    {
        try
        {
            var node = root?.SelectSingleNode("ClassInfo");
            if (XmlHelper.GetNodeAttributeInt(node, "Grade", out var val))
            {
                ClassInfo.Grade = val;
            }

            if (XmlHelper.GetNodeAttributeInt(node, "Class", out val))
            {
                ClassInfo.Class = val;
            }

            var nodeList = node?.SelectNodes("Student");
            if (nodeList == null || nodeList.Count == 0)
            {
                return;
            }

            foreach (XmlNode item in nodeList)
            {
                if (XmlHelper.GetNodeAttributeStr(item, "Name", out var str) &&
                    XmlHelper.GetNodeAttributeInt(item, "Age", out val))
                {
                    ClassInfo.StudentInfoList.Add(new StudentInfo(str, val));
                }
            }
        }
        catch (Exception e)
        {
            Logger.Error($@"加载班级信息异常:{e.Message}.");
        }
    }

    #endregion

    #region 设置参数

    public void SetDatabaseConfig(string ip, string catalog, string user, string password)
    {
        DbIp = ip;
        DbCatalog = catalog;
        DbUser = user;
        DbPassword = password;

        //Get Config file
        var info = new DirectoryInfo(Assembly.GetExecutingAssembly().Location);
        var xmlFilePath = Path.Combine(info.Parent?.FullName ?? string.Empty, _configName);

        //从配置文件读取数据
        var xmlDoc = new XmlDocument();
        try
        {
            xmlDoc.Load(xmlFilePath);
            var node = xmlDoc.SelectSingleNode("/AppSettings/Database");
            XmlHelper.SetNodeAttributeValue(node, "Ip", ip);
            XmlHelper.SetNodeAttributeValue(node, "Catalog", catalog);
            XmlHelper.SetNodeAttributeValue(node, "User", user);
            XmlHelper.SetNodeAttributeValue(node, "Password", password);
            xmlDoc.Save(xmlFilePath);
        }
        catch (Exception e)
        {
            Logger.Error($@"设置数据库参数失败:{e.Message}.");
            throw;
        }
    }

    #endregion

    #region 单例模式

    private static XmlConfigManager _instance;

    private static readonly object LockInstanceHelper = new object();

    private XmlConfigManager()
    {
        ClassInfo = new ClassInfo();
    }

    public static XmlConfigManager Instance
    {
        get
        {
            if (_instance != null)
            {
                return _instance;
            }

            lock (LockInstanceHelper)
            {
                _instance = _instance ?? new XmlConfigManager();
            }

            return _instance;
        }
    }

    #endregion
}