跳转到主要内容
杨月昌的软件博客
返回

WPF 进阶:使用 XmlDocument 实现 XML 文件的深度读写

1. 概述

在 WPF 开发中,XML 常用于存储本地配置、离线数据或简单的报文交互。虽然现代开发中 JSON 流行,但 XML 在处理具有复杂嵌套关系的结构化数据时依然具有无可比拟的优势。

本文将通过封装 XmlReader(读取流)和 XmlWriter(写入流)两个工具类,演示如何快速操作 XML 文档对象模型 (DOM)。


2. 封装 XmlReader:高效检索数据

XmlReader 类封装了常用的 Xpath 检索和节点转换逻辑。

using System.Xml;

namespace WpfApp4
{
    public static class XmlReader
    {
        private static XmlDocument _doc = new XmlDocument();

        /// <summary>
        /// 加载XML文件
        /// </summary>
        public static void LoadXml(string url)
        {
            if (System.IO.File.Exists(url))
                _doc.Load(url);
        }

        public static XmlElement GetRoot() => _doc.DocumentElement;

        /// <summary>
        /// 获取指定节点的属性值
        /// </summary>
        public static string GetAttribute(XmlNode node, string attrName)
        {
            return (node as XmlElement)?.GetAttribute(attrName) ?? string.Empty;
        }

        /// <summary>
        /// 使用 XPath 获取子节点的文本内容
        /// </summary>
        public static string GetNodeInnerText(XmlNode node, string childName)
        {
            return node.SelectSingleNode(childName)?.InnerText ?? string.Empty;
        }
    }
}

3. 封装 XmlWriter:构建结构化文档

XmlWriter 简化了节点的创建与层级挂载。

using System.Xml;

namespace WpfApp4
{
    public static class XmlWriter
    {
        private static XmlDocument _doc = new XmlDocument();

        public static void InitializeDoc() => _doc = new XmlDocument();

        /// <summary>
        /// 创建并挂载根节点
        /// </summary>
        public static XmlElement CreateRoot(string rootName)
        {
            InitializeDoc();
            XmlElement root = _doc.CreateElement(rootName);
            _doc.AppendChild(root);
            return root;
        }

        /// <summary>
        /// 为节点添加属性
        /// </summary>
        public static void AddAttribute(XmlElement element, string name, string value)
        {
            XmlAttribute attr = _doc.CreateAttribute(name);
            attr.Value = value;
            element.Attributes.Append(attr);
        }

        /// <summary>
        /// 创建子节点
        /// </summary>
        public static XmlNode CreateNode(string name, string text)
        {
            XmlNode node = _doc.CreateElement(name);
            node.InnerText = text;
            return node;
        }

        /// <summary>
        /// 保存并添加 XML 声明
        /// </summary>
        public static void Save(string path)
        {
            if (_doc.FirstChild.NodeType != XmlNodeType.XmlDeclaration)
            {
                XmlDeclaration dec = _doc.CreateXmlDeclaration("1.0", "UTF-8", null);
                _doc.InsertBefore(dec, _doc.DocumentElement);
            }
            _doc.Save(path);
        }
    }
}

4. 前端界面与交互逻辑

XAML 布局

<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
    <Button Content="一键生成国家数据" Click="btnCreatXml" Margin="10" Padding="10"/>
    <Button Content="读取第一个首都" Click="btnReadXml" Margin="10" Padding="10"/>
</StackPanel>

后台逻辑实现

private void btnCreatXml(object sender, RoutedEventArgs e)
{
    try
    {
        XmlElement root = XmlWriter.CreateRoot("WorldData");
        
        // 数据准备
        var countries = new[] { 
            new { Name = "中国", Capital = "北京" },
            new { Name = "美国", Capital = "华盛顿" } 
        };

        foreach (var c in countries)
        {
            XmlElement node = (XmlElement)XmlWriter.CreateNode("Country", "");
            XmlWriter.AddAttribute(node, "Name", c.Name);
            XmlWriter.AddAttribute(node, "Capital", c.Capital);
            root.AppendChild(node);
        }

        XmlWriter.Save("data.xml");
        MessageBox.Show("XML 文件已成功创建于运行目录!");
    }
    catch (Exception ex)
    {
        MessageBox.Show($"写入错误: {ex.Message}");
    }
}

private void btnReadXml(object sender, RoutedEventArgs e)
{
    try
    {
        XmlReader.LoadXml("data.xml");
        XmlElement root = XmlReader.GetRoot();
        
        // 读取第一个子节点的属性
        if (root?.HasChildNodes == true)
        {
            string capital = XmlReader.GetAttribute(root.FirstChild, "Capital");
            MessageBox.Show($"读取到的首都是: {capital}");
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show($"读取错误: {ex.Message}");
    }
}

5. 总结

通过封装,我们将繁琐的 XML 节点操作抽象成了可读性更强的业务方法。在实际项目中,这种封装有助于统一 XML 格式,减少硬编码。



上一篇
C# 常用时间范围获取指南:当天、周、月、上月
下一篇
深度直击:后向传播算法(Backpropagation)全解析