赶紧写完,不然就忘了

仅代表本新手的个人小结,如有错误恳请指出!

示例代码均(大概)来自https://github.com/DiamondMofeng/HFUT-AutoSignGUI

微软官方最详细的文档:https://docs.microsoft.com/zh-cn/dotnet/api/system.xml(个人觉得还是某些地方有纰漏..)

XmlDocument

XmlDocument可通过文件路径或字符串加载xml至内存中。

好处是方便易用,坏处是可能占用较多的内存。

继承自XmlNode类,所以可以用XmlNode类的属性或方法来对内部进行操作

对xml内已有内容进行修改

下列代码是一个实例,加载模板xml,对指定内容进行修改之后保存为新xml。

通过doc的DocumentElement属性获取整个xml文件的所有元素,再用以xpath的方式选择对应元素(XmlNode.SelectSingleNode(xpath) ,直接对内部文本进行修改,最后保存修改后的XmlDocument.

public static void GenerateTaskXMLFromPattern(string PathXMLpattern, string PathXMLExport
       , string taskID
       , string hh , string mm
       , string TimerEnabled
       , string LogonEnabled
       , string ExecPath
       , string Arguments
       , string DirectoryPath)
        {


            XmlDocument doc = new XmlDocument();
            doc.Load(PathXMLpattern);

            // 加载命名空间,因为模板文档里面带了命名空间
            XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
            nsmgr.AddNamespace("p", "http://schemas.microsoft.com/windows/2004/02/mit/task");



            XmlNode node;
            XmlNode root = doc.DocumentElement;

            //taskID
            node = root.SelectSingleNode("/p:Task/p:RegistrationInfo/p:URI", nsmgr);
            node.InnerText = "\\HFUT\\" + taskID;
            //运行时间
            if (TimerEnabled == "true")
            { 
            node = root.SelectSingleNode("/p:Task/p:Triggers/p:CalendarTrigger/p:StartBoundary", nsmgr);
            node.InnerText = "2021-01-01T" + hh + ":" + mm + ":00";
            }
            //是否定时运行
            node = root.SelectSingleNode("/p:Task/p:Triggers/p:CalendarTrigger/p:Enabled", nsmgr);
            node.InnerText = TimerEnabled;
            //是否登陆时运行
            node = root.SelectSingleNode("/p:Task/p:Triggers/p:LogonTrigger/p:Enabled", nsmgr);
            node.InnerText = LogonEnabled;
            //要运行的软件的路径
            node = root.SelectSingleNode("/p:Task/p:Actions/p:Exec/p:Command", nsmgr);
            node.InnerText = ExecPath;
            //启动项参数
            node = root.SelectSingleNode("/p:Task/p:Actions/p:Exec/p:Arguments", nsmgr);
            node.InnerText = Arguments;
            //工作目录
            node = root.SelectSingleNode("/p:Task/p:Actions/p:Exec/p:WorkingDirectory", nsmgr);
            node.InnerText = DirectoryPath;


            //写入用户id,以通过安全性检查;无需外部参数
            node = root.SelectSingleNode("/p:Task/p:Principals/p:Principal/p:UserId", nsmgr);
            node.InnerText = Environment.UserName;

            doc.Save(PathXMLExport);
            //doc.Save(Console.Out);
            //Console.ReadKey(true);
        }

向xml内添加新节点、属性

下面的实例中,xml是已在别处加载好的文档,含有根节点<TaskList/>

public static void addTaskToXML(XmlDocument xml, string TaskID, string account, string mode, string hh, string mm)
{
    //为TaskList添加元素Task
    XmlNode TaskList = xml.SelectSingleNode("TaskList");//查找<TaskList>节点
    XmlElement Task = xml.CreateElement("Task");//创建一个<Task>元素节点
    Task.SetAttribute("id", TaskID);//设置该节点id属性
    TaskList.AppendChild(Task);//添加<Task>到<TaskList>节点中
    //为Task添加内容
    XmlElement acc = xml.CreateElement("account");//添加<account>元素节点
    acc.InnerText = account;//设置<account>元素节点的文本节点内容
    Task.AppendChild(acc);//添加<account>到<Task>节点中

    XmlElement solution = xml.CreateElement("solution");
    solution.InnerText = mode;
    Task.AppendChild(solution);

    XmlElement time = xml.CreateElement("time");
    time.InnerText = hh + ":" + mm;
    Task.AppendChild(time);
}

删除Xml内节点

下面的代码实例了一个根据变量taskID,匹配xml内节点的ID属性符合条件的节点,并进行删除。

(最后保存覆盖原文件)

public static void DeleteTaskInXML(string XMLPath, string taskID)
        {
            XmlDocument xml = new XmlDocument();
            xml.Load(XMLPath);

            XmlElement taskToRemove = xml.GetElementById(taskID);

            xml.DocumentElement.RemoveChild(taskToRemove);

            xml.Save(XMLPath);

        }

XmlReader

XmlReader是一个抽象类,但可以通过XmlReader.Create()方法来创建对象

如官方文档所写, “XmlReader 表示提供对 XML 数据进行快速、非缓存、只进访问的读取器。”,是一个流式读取。

可以用.Read()、.MoveTo……()等方法让读取器移动至想要的地方,此外还有丰富的属性和方法供使用

(官方文档https://docs.microsoft.com/zh-cn/dotnet/api/system.xml.xmlreader

用XmlReader遍历xml指定内容并输出

下面是一个例子,用XmlReader遍历xml文件中的指定节点,并输出(这里是输出到ListView中)

这是用到的XML文件

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TaskList[
<!ELEMENT Task ANY>
<!ATTLIST Task id ID #REQUIRED>
]>
<TaskList>
  <Task id="test01">
    <account>123456789</account>
    <solution>定时打卡</solution>
    <time>22:00</time>
  </Task>
  <Task id="test03">
    <account>123456789</account>
    <solution>开机时&amp;定时</solution>
    <time>22:30</time>
  </Task>
  <Task id="test02">
    <account>987654321</account>
    <solution>定时打卡</solution>
    <time>22:20</time>
  </Task>
</TaskList>

这是代码

用到了.ReadSubTree()方法,在主Reader经过Task节点时为其创建一个针对Task节点的子Reader,再用子Reader对Task节点内元素进行遍历

(注:读取节点属性时,.MoveToAttribute()方法不知道为何无效,官方文档中也没有写清楚使用案例,我这里换用了.MoveToContent()和.GetAttribute()两个方法来实现获取节点属性)

public static void LoadTasks(string xmlPath, System.Windows.Forms.ListView listView)
        {
            //先清空listview
            foreach (System.Windows.Forms.ListViewItem item in listView.Items)
            {
                listView.Items.Remove(item);
            }


            //打开XMLTasks.xml
            XmlReaderSettings set = new XmlReaderSettings();
            set.DtdProcessing = DtdProcessing.Parse;
            using (XmlReader reader = XmlReader.Create(xmlPath, set))
            {
                //Console.WriteLine("test");//debug
                while (reader.ReadToFollowing("Task"))
                {
                    XmlReader taskReader = reader.ReadSubtree();
                    string id = "";
                    string acc = "";
                    string mode = "";
                    string time = "";
                    if (taskReader.HasAttributes == true)
                    {
                        //taskReader.MoveToAttribute(0);//这个函数有毛病
                        taskReader.MoveToContent();
                        id = taskReader.GetAttribute("id");
                    }
                    while (taskReader.Read())
                    {   
                        if (taskReader.NodeType == XmlNodeType.Whitespace) continue;
                        if (taskReader.NodeType == XmlNodeType.Element)
                        {
                            switch (taskReader.Name)
                            {
                                case "account":
                                    acc = reader.ReadElementContentAsString();
                                    continue;
                                case "solution":
                                    mode = reader.ReadElementContentAsString();
                                    continue;
                                case "time":
                                    time = reader.ReadElementContentAsString();
                                    continue;
                            }
                        }
                    }
                    System.Windows.Forms.ListViewItem listViewItem_Task = new System.Windows.Forms.ListViewItem(new string[] {
                            id,
                            acc,
                            mode,
                            time}, -1);

                    listView.Items.Add(listViewItem_Task);
                }
            }
        }

输出结果(ListView设定了根据ID自动升序排列):

Xml.Linq

说在前面:我是一个纯noob,sql用的也不熟。。

Linq大概是微软的.Net系语言中独特的查询语言,和sql既相似又有很大差异(废话)

官方文档:

Linq to xml:https://docs.microsoft.com/zh-cn/dotnet/api/system.xml.linq

Linq:https://docs.microsoft.com/zh-cn/dotnet/api/system.linq

官方指导:

C# 中的 LINQ:https://docs.microsoft.com/zh-cn/dotnet/csharp/linq/linq-in-csharp

用Linq查询xml中符合条件的项

本例为根据string taskID,查询XML中是否已存在指定的ID,防止在添加新节点时发生ID重复的情况。

public static bool isTaskIDExists(string taskID, string XMLPath)
        {
            //试试Linq
            XDocument xdoc = XDocument.Load(XMLPath);
            var queryIDs =
                from att_id in xdoc.Element("TaskList").Elements("Task").Attributes("id")
                where att_id.Value == taskID
                select att_id.Value;
            if (queryIDs.Take(1).Count() >= 1)
            {
                return true;
            }
            else return false;
        }

其他

写的很肤浅,只涉及了基本简单的使用…………

但无论怎样,这是自己的经验总结,从这里出发,慢慢扩展自己的知识…………


I am a noob