C#读写XML文件工具类——满足一切需求
还是由于工作的需要,要进行解析XML配置表,虽然感觉XML很简单,因为之前对XML完全空白,所以只能从头学起,那就只能从google开始,找到一些参考①②③见文末,但是总感觉比较零散、混乱,功能不全,只能是例子不能成为工具,所以一直想找一个基本可以满足所有需求的实现。大概熟悉了XML的结构(还是花了好久,虽然比较简单,但是要很C#api中的Node,Element,Attribute等概念对应分得清要一点时间去消化)和C# XmlDocument的api,感觉通过api去实现一些功能还是比较困难或者比较直观,比如我要查找含有属性“lang”的所有结点,这个大概想了下要得遍历,所以迟迟未动手,知道我找到参考④。看了④的文章,很是惊奇,惊呼——原来还有这个东西(Xpath),顿时觉得希望实现自己想要的功能。然后就去查阅W3C School的Xpath的规范。
有了想法之后,总是忍不住的想动手,刚开始的想法是定义一个StringBuilder xpath变量,然后往后面添加相关操作的Xpath字符串,但是还是感觉情况太多了,函数命名都成问题了(我记得一直命名到AddNode9()),总感觉做的是字符匹配的过程,但是有没有“正则表达式”的智能,然后我有想起一直的困惑——对于情况很多再加上不同情况的组合的问题解决起来很被动,有种被耍的感觉。那只有彻底解决,其实这就是“正则表达式”的原理——有限状态机,好在我之前看过“有限状态机”的理论。
我早上三点起来(哎,一直挺矛盾的),我在CodeProject上找FSM的代码(还真不少),果断下载了几个,对代码仔细研读一番,最后感觉这个问题用FSM实在是大材小用,发现之前感觉的“不智能”都不会了,可以通过约束使用规范避免,所以还是回归到Xpath上,然后就有了下面的代码(测试了几个都没有问题),后面部分(有注释的直接copy③),实在是没有时间(一会还要去上班),然后就是功能还不完善,以后有时间一并补上(如果你有补充希望能够分享下,谢谢)。
下面的代码(假定你具有Xpath的基础)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Xml; /// Code By D.S.Qiu namespace XmlParse { class XmlUtils { public static StringBuilder xpath = new StringBuilder(); public static XmlDocument xmlDoc=new XmlDocument(); public static XmlElement root = null; public static void AddCommint(string commit) { xpath.Append(commit); } public static void AddNode(string name) { xpath.Append("/" + name); } public static void AddNodeRegardlessPosition(string name) { xpath.Append("//" + name); } public static void AddNodeByIndex(string name, string index) { xpath.Append("/" + name + "[" + index + "]"); } public static void AddNodeByIndexRegardlessPosition(string name, string index) { xpath.Append("//" + name + "[" + index + "]"); } public static void AddNodeByValue(string name, string value, string operatorstr = "=") { xpath.Append("[" + name + operatorstr + value + "]"); } public static void AddIndex(string index) { xpath.Append("[" + index + "]"); } public static void AddAttribute(string name) { xpath.Append("[@" + name + "]"); } public static void AddAttributeByValue(string name, string value,string operatorstr="=") { xpath.Append("[@" + name +operatorstr+value+ "]"); } public static void AddAnyNode() { xpath.Append("/*" ); } public static void AddAnyNodeRegardlessPosition() { xpath.Append("//*"); } public static void AddAnyAttribute() { xpath.Append("[@*]"); } public static void Load(string filename) { xmlDoc.Load(filename); root = xmlDoc.DocumentElement; } public static XmlNodeList Commit(string commit=null) { XmlNodeList xn=null; if (commit != null) { xn= root.SelectNodes(commit); } else if (xpath.Length != 0) { xn= root.SelectNodes(XmlUtils.xpath.ToString()); xpath.Clear(); } return xn; } public void addElement(string path, string node_root, string node_name, string node_text, string att_name, string att_value) { Load(path); XmlNodeList nodeList = xmlDoc.SelectSingleNode(node_root).ChildNodes;//获取bookstore节点的所有子节点 //判断是否有节点,有节点就遍历所有子节点,看看有没有重复节点,没节点就添加一个新节点 if (nodeList.Count > 0) { foreach (XmlNode xn in nodeList)//遍历所有子节点 { XmlElement xe = (XmlElement)xn;//将子节点类型转换为XmlElement类型 if (xe.GetAttribute(att_name) != att_value) { XmlNode xmldocSelect = xmlDoc.SelectSingleNode(node_root); //选中根节点 XmlElement son_node = xmlDoc.CreateElement(node_name); //添加子节点 son_node.SetAttribute(att_name, att_value); //设置属性 son_node.InnerText = node_text; //添加节点文本 xmldocSelect.AppendChild(son_node); //添加子节点 xmlDoc.Save(path); //保存xml文件 break; } } } else { XmlNode xmldocSelect = xmlDoc.SelectSingleNode(node_root); //选中根节点 XmlElement son_node = xmlDoc.CreateElement(node_name); //添加子节点 son_node.SetAttribute(att_name, att_value); //设置属性 son_node.InnerText = node_text; //添加节点文本 xmldocSelect.AppendChild(son_node); //添加子节点 xmlDoc.Save(path); //保存xml文件 } } /// <summary> /// 修改节点的内容 /// </summary> /// <param name="path">xml文件的物理路径</param> /// <param name="node_root">根节点名称</param> /// <param name="new_text">节点的新内容</param> /// <param name="att_name">节点的属性名</param> /// <param name="att_value">节点的属性值</param> public void UpdateElement(string path, string node_root, string new_text, string att_name, string att_value) { Load(path); XmlNodeList nodeList = xmlDoc.SelectSingleNode(node_root).ChildNodes;//获取bookstore节点的所有子节点 foreach (XmlNode xn in nodeList)//遍历所有子节点 { XmlElement xe = (XmlElement)xn;//将子节点类型转换为XmlElement类型 if (xe.GetAttribute(att_name) == att_value) { xe.InnerText = new_text; //内容赋值 xmlDoc.Save(path);//保存 break; } } } /// <summary> /// 删除节点 /// </summary> /// <param name="path">xml文件的物理路径</param> /// <param name="node_root">根节点名称</param> /// <param name="att_name">节点的属性名</param> /// <param name="att_value">节点的属性值</param> public void deleteNode(string path, string node_root, string att_name, string att_value) { Load(path); XmlNodeList nodeList = xmlDoc.SelectSingleNode(node_root).ChildNodes; XmlNode root = xmlDoc.SelectSingleNode(node_root); foreach (XmlNode xn in nodeList) { XmlElement xe = (XmlElement)xn; if (xe.GetAttribute(att_name) == att_value) { //xe.RemoveAttribute("name");//删除name属性 xe.RemoveAll();//删除该节点的全部内容 root.RemoveChild(xe); xmlDoc.Save(path);//保存 break; } } } } }
最后说下自己的感悟:因为刚毕业,工作中的很多问题都没有接触过,虽然都很简单,但是要做出来还是要花一定时间的,所以D.S.Qiu觉得学一个新东西一定要彻底弄懂,以后才能事半功倍,还要记录一些学习过程中遇到的问题和解决问题的一些方法和心得。
增补于2015.12.08
之前因为后面找到XmlParser(查看博客),因为一直都只是Read(解析)没有Write,然后最近发布Window Phone 版本需要修改Unity 导出的 vs 工程的 .csproj 文件,其实就是一个xml文件,一开始都是google,最后还是发现自己很早写这篇XPath的博客,然后就打算整理下一个工具类。本来打算自己实现一个 XPath 类以实现链式调用(chain call) ,结果折腾到今天凌晨5点,才发现其实完全没有必要或者做不到想象中的各种爽,最后把函数整理了下,不得不睡觉……
没睡几个小时,一大早来公司,测试,发现各种返回为 null ,明明和别人的 XPath 是一样,就是得不到结果。
一直在google 最后发现还是有命名空间的问题,然后最后用 XPathNavigator.Evaluate() 返回 XPathNodeIterator 的 Count 一直为0,发现要调用 MoveNext 才能获得值。
花了一天才解决上面两个问题,最后才发现MSDN上面都有说道,所以一定要细致,不放过任何细节,不放过任何细节,不放过任何细节……
整理下附上最新的代码:
using System.Text; using System.Xml; /// Code By D.S.Qiu /* * 具体规则可参考:http://www.w3school.com.cn/xpath/index.asp * 或者:https://msdn.microsoft.com/en-us/library/ms256471(v=vs.110).aspx * https://msdn.microsoft.com/en-us/library/ms256453(v=vs.110).aspx * * 1.节点 * a)./author 或 author 当前节点的所有<author>子节点 * b)/author 根节点的所有<author>子节点 * c)//author 根节点的所有<author>节点 注 .//author 当前节点所有<author>节点 * d). 选取当前节点 * e).. 选取当前节点的父节点 * f)@ 选取属性 author/first-name All <first-name> elements within an <author> element of the current context node. bookstore//title All <title> elements one or more levels deep in the <bookstore> element (arbitrary descendants). .//title All <title> elements one or more levels deep in the current context. Note that this situation is essentially the only one in which the period notation is required. @style The style attribute of the current element context. price/@exchange The exchange attribute of <price> elements within the current context. book/@style The style attribute of all <book> elements. Note that the following example is not valid, because an attribute cannot have any children. price/@exchange/total * * 2.谓词 * a)[] 谓语被嵌在方括号中 * /bookstore/book[price>35.00] 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。 * /bookstore/book[position()<3] 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。 * //title[@lang='eng'] 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。 * book[excerpt] All <book> elements that contain at least one <excerpt> element. book[excerpt]/title All <title> elements inside <book> elements that contain at least one <excerpt> element. book[excerpt]/author[degree] All <author> elements that contain at least one <degree> element, and are inside of <book> elements that contain at least one <excerpt> element. book[author/degree] All <book> elements that contain at least one <author> element with at least one <degree> child element. book[excerpt][title] All <book> elements that contain at least one <excerpt> element and at least one <title> element. * b)* 匹配任何元素节点 * c)@* 匹配任何属性节点 * d)node() 匹配任何类型的节点 * * 3.轴 * a)ancestor 选取当前节点的所有先辈(父、祖父等)。 * b)ancestor-or-self 选取当前节点的所有先辈(父、祖父等)以及当前节点本身。 * c)attribute 选取当前节点的所有属性。 * d)child 选取当前节点的所有子元素。 * e)descendant 选取当前节点的所有后代元素(子、孙等)。 * f)descendant-or-self 选取当前节点的所有后代元素(子、孙等)以及当前节点本身。 * g)following 选取文档中当前节点的结束标签之后的所有节点。 * h)namespace 选取当前节点的所有命名空间节点。 * i)parent 选取当前节点的父节点。 * j)preceding 选取文档中当前节点的开始标签之前的所有节点。 * k)preceding-sibling 选取当前节点之前的所有同级节点。 * l)self 选取当前节点。 * 步的语法: 轴(axis) 定义所选节点与当前节点之间的树关系 节点测试(node-test) 识别某个轴内部的节点 零个或者更多谓语(predicate) 更深入地提炼所选的节点集 轴名称::节点测试[谓语] 实例 child::book 选取所有属于当前节点的子元素的 book 节点。 attribute::lang 选取当前节点的 lang 属性。 child::* 选取当前节点的所有子元素。 和 * 一致 child::text() 选取当前节点的所有文本子节点。 和 . 一致 child::node() 选取当前节点的所有子节点。 和 .. 一致 * * Precedence Precedence order (from highest precedence to lowest) between Boolean and comparison operators is shown in the following table. Precedence Operators Description 1 ( ) Grouping 2 [ ] Filters 3 / Path operations // 4 < Comparisons <= > >= 5 = Comparisons != 6 | Union 7 not() Boolean not 8 and Boolean and 9 or Boolean or * */ using UnityEngine; using System; using System.Collections.Generic; using System.IO; using System.Runtime.CompilerServices; using System.Xml.XPath; public static class XmlExtension { public static XmlNode UpdateAttribute(this XmlNode node,string name,string value) { XmlAttribute newAttr = node.OwnerDocument.CreateAttribute(name); newAttr.Value = value; //xn.Attributes.RemoveNamedItem(attributeValues[i]); node.Attributes.SetNamedItem(newAttr); //设置属性 return node; } public static XmlNode DeleteAttribute(this XmlNode node, string name) { if (node.Attributes == null) { Debug.Log("This XMLNode have no attribute!"); } else { XmlAttribute attribute = node.Attributes[name]; node.Attributes.Remove(attribute); } return node; } public static XmlNode DeleteElement(this XmlNode node,string xpath) { XmlNodeList rootNodes = node.SelectNodes(xpath); if(rootNodes == null || rootNodes.Count == 0) { Debug.Log(string.Format("Not Found the node {0} in the XmlNode!",xpath)); return node; } //遍历所有子节点 foreach (XmlNode xn in rootNodes) { node.RemoveChild(xn); } return node; } /// <summary> /// /// </summary> /// <param name="node"></param> /// <param name="xpath"></param> /// <param name="nodeName"></param> /// <param name="nodeText"></param> /// <returns>方便chain call</returns> public static System.Xml.XmlElement AppendElement(this XmlNode node,string xpath,string nodeName,string nodeText) { XmlNode rootNode = node.SelectSingleNode(xpath); if(rootNode == null ) { Debug.Log(string.Format("Not Found the node {0} in the XmlNode!",xpath)); return null; } XmlElement sonNode = node.OwnerDocument.CreateElement(nodeName); //添加子节点 sonNode.InnerText = nodeText; rootNode.AppendChild(sonNode); //添加子节点 return sonNode; } public static System.Xml.XmlElement AppendElement(this XmlNode node, string nodeName, string nodeText = "") { XmlElement sonNode = node.OwnerDocument.CreateElement(nodeName); //添加子节点 sonNode.InnerText = nodeText; node.AppendChild(sonNode); //添加子节点 return sonNode; } } /// <summary> /// 默认把xml的命名工具都去掉,方便使用 XPath 表达式,保存之后再替换回来 /// 所有解析必须在Begin 和 End 之间调用 /// </summary> public class XmlUtility { /*public class XPath { public const string FilterFormat = "[{0}]"; public const string StepFormat = "{0}::{1}[{2}]"; private static StringBuilder s_content = new StringBuilder(); public XPath Clear() { s_content = new StringBuilder(); return this; } public XPath Append(string path) { s_content.Append (path); return this; } public XPath AppendFormat(string format,params string[] param) { return Append(string.Format(format, param)); } public XPath AppendNode(string nodeName) { return Append("/" + nodeName); } public XPath AppendAnyNode(string nodeName) { return Append("//" + nodeName); } public XPath AppendFilter(string filter) { return AppendFormat(FilterFormat, filter); } public XPath AppendStep(string axis,string nodeTest ="*",string predicate = "*") { return AppendFormat (StepFormat, axis, nodeTest, predicate); } }*/ public const string Xmlns = "xmlns"; public const string XmlnsReplace = "xmlns_replace"; private static XmlDocument xmlDoc = null;//new XmlDocument(); private static bool replaceXmlns = false; private static string xmlPath; public static void Begin(string xmlPath, bool replaceXmlns = true) { XmlUtility.xmlPath = xmlPath; XmlUtility.replaceXmlns = replaceXmlns; Load(xmlPath, replaceXmlns); } public static void End(bool save = true) { if(save) Save(XmlUtility.xmlPath,XmlUtility.replaceXmlns); xmlDoc = null; } private static void Load(string xmlPath,bool replaceXmlns) { xmlDoc = new XmlDocument (); try { string xmlText = File.ReadAllText(xmlPath); //去掉命名空间 if (replaceXmlns) xmlText = xmlText.Replace(Xmlns, XmlnsReplace); xmlDoc.LoadXml(xmlText); } catch(Exception ex) { Debug.LogError("XmlDocument Load Error: " + ex.ToString()); } } private static void Save(string xmlPath, bool replaceXmlns) { if (xmlDoc == null) return ; try { xmlDoc.Save(xmlPath); //恢复默命名空间 if (replaceXmlns) { string xmlText = File.ReadAllText(xmlPath); xmlText = xmlText.Replace(XmlnsReplace,Xmlns); File.WriteAllText(xmlPath,xmlText); } } catch (Exception ex) { Debug.LogError("XmlDocument Save Error: " + ex.ToString()); } } /// <summary> /// The result of the expression (Boolean, number, string, or node set). This maps to Boolean, Double, String, or /// XPathNodeIterator objects respectively. /// 注意:如果返回的是 XPathNodeIterator 不会获得所有元素,必须帝调用MoveNext()才能取得元素 /// 查看:https://msdn.microsoft.com/en-us/library/system.xml.xpath.xpathnodeiterator.aspx 的 Remarks /// </summary> /// <param name="xmlPath">Xml path.</param> /// <param name="xpath">Xpath.</param> public static object Evaluate(string xpath) { //XmlDocument xmlDoc = Load (xmlPath); if (xmlDoc == null) return null; XPathNavigator navigator = xmlDoc.CreateNavigator(); try { XPathExpression expression = navigator.Compile(xpath); return navigator.Evaluate(expression); } catch (Exception ex) { Debug.LogError("XPath Evalute Error: " + ex.ToString()); return null; } } public static XmlNode FindElement(string xpath) { //XmlDocument xmlDoc = Load (xmlPath); if (xmlDoc == null) return null; var test = xmlDoc.SelectNodes (xpath); return xmlDoc.SelectSingleNode (xpath); } public static XmlNodeList FindElements(string xpath) { //XmlDocument xmlDoc = Load (xmlPath); if (xmlDoc == null) return null; return xmlDoc.SelectNodes (xpath); } public static void AppendElement(string xpath,string nodeName,string nodeText) { //XmlDocument xmlDoc = Load (xmlPath); if (xmlDoc == null) return; xmlDoc.AppendElement (xpath,nodeName,nodeText); //xmlDoc.Save (xmlPath); } public static void DeleteElement(string xpath) { //XmlDocument xmlDoc = Load (xmlPath); if (xmlDoc == null) return; xmlDoc.DeleteElement (xpath); //xmlDoc.Save (xmlPath); } }
如果您对D.S.Qiu有任何建议或意见可以在文章后面评论,或者发邮件(gd.s.qiu@gmail.com)交流,您的鼓励和支持是我前进的动力,希望能有更多更好的分享。
转载请在文首注明出处:http://dsqiu.iteye.com/blog/1888476
更多精彩请关注D.S.Qiu的博客和微博(ID:静水逐风)
参考:
①jackyong: http://www.cnblogs.com/liuyong/archive/2011/01/26/Jackyong.html
②Eric Sun: http://www.cnblogs.com/mingmingruyuedlut/archive/2011/01/26/1945107.html
③永恒浪子: http://www.cnblogs.com/hulang/archive/2010/12/30/1922298.html
④顶天的蚂蚁: http://www.cnblogs.com/RiseSoft/archive/2012/03/17/2404007.html
⑤W3C School: http://www.w3school.com.cn/xpath/xpath_syntax.asp
相关推荐
以上就是C#中读写XML文件的基本方法和一些高级操作,通过这些工具,你可以轻松地处理XML数据,无论是读取、修改还是创建XML文档。在实际开发中,根据具体需求选择合适的方法,可以提高代码的效率和可维护性。
以上就是C#中读写XML文件的基本操作,涵盖了增加、删除、修改和查找元素的方法。在实际应用中,还需要注意错误处理、文件锁定等问题,确保代码的健壮性。同时,根据具体需求,可以选择合适的XML处理方式,如DOM、SAX...
总的来说,C#提供的XML处理工具强大且灵活,能够满足各种XML操作需求,无论是简单的数据存储还是复杂的Web服务交互,都是开发者不可或缺的工具。通过深入学习和实践,开发者可以熟练地驾驭XML,实现高效的数据处理。
总之,C#提供了强大的工具和API来处理XML文件,无论是简单的读写操作还是复杂的XML结构处理,都能得心应手。通过实例118的源码,你可以进一步学习和实践这些概念,掌握C#与XML交互的精髓。在实际项目中,灵活运用...
本文将详细介绍如何使用C#来读取XML文件、添加和删除节点,并将XML文件内容显示在ListView控件中。 首先,我们需要引入System.Xml命名空间,它包含了处理XML文档所需的所有类和方法。在C#代码中,可以使用以下语句...
在.NET框架中,C#提供了一种强大的工具——Language Integrated Query (LINQ),它使得数据查询变得更加简洁和高效。在处理XML文档时,LINQ to XML(也称为LinqXML)是一个非常实用的API,它提供了面向对象的方式来...
通过理解和掌握C#读写配置文件的技巧,开发者能够更好地适应应用程序的需求变化,同时保持代码的灵活性和可维护性。提供的源代码是一个很好的起点,可以帮助初学者理解这一过程,并为他们自己的项目提供灵感。
总之,C#提供了丰富的工具和API来处理XML文件,涵盖了从基本的读写操作到复杂的查询、序列化和验证。通过熟练掌握这些技术,开发者可以有效地处理XML数据,实现数据的存储和传输。在实际项目中,选择合适的XML处理...
在"实例118 如何读写XML文件"中,我们可以看到一个具体的代码示例,展示如何使用以上方法读取和写入XML文件。这个实例可能包括创建一个简单的XML文件,添加一些元素和属性,然后读取这些元素并打印它们的值,最后...
C#提供了System.Xml命名空间,其中的XmlWriter类是用于创建XML文档的主要工具。以下是一个简单的示例: ```csharp using System; using System.IO; using System.Xml; public class XmlWriteExample { public ...
【C# 读写XML文件】 XML(Extensible Markup Language)是一种结构化数据格式,广泛应用在数据交换、配置文件和存储轻量级数据中。C#提供了丰富的类库来支持XML的处理,使得程序员可以方便地进行XML的读取和写入操作...
本文将深入探讨C#和C语言中如何读取和写入XML文件,以及XML读写的重要性。 首先,让我们从C#开始。在.NET框架中,C#提供了丰富的API来处理XML,主要通过System.Xml命名空间。以下是一些核心类: 1. **XmlDocument*...
在C#中,我们可以利用.NET Framework提供的`System.Xml`命名空间中的类来读写XML。以下是一些关键类: 1. **XmlDocument**: 这个类提供了一种DOM(文档对象模型)方法来处理XML。通过加载XML文件,我们可以修改节点...
- VS自带了一些XML工具,如XML编辑器、XML验证器、XML架构设计器等,它们能帮助开发者高效地处理XML文件。 10. **调试XML**: - 当XML数据与Web服务或SOAP通信时,VS的“XML”工具窗口可以用来查看和调试XML消息...
在这个“C#读写XML的例子”中,我们可以通过一个完整的Visual Studio(VS)工程来学习这一过程。 首先,我们需要了解C#中处理XML的基本类库:System.Xml。这个命名空间包含了多个类,如XmlDocument、XmlNode、...
下面将详细讲解如何在C#中进行XML的读写操作,并结合“XMLDemo”这个示例项目进行说明。 首先,我们需要引入System.Xml命名空间,它包含了处理XML的基本类库。在Visual Studio 2008或更高版本中,使用.NET ...
XML(eXtensible Markup Language)是一种用于标记数据的语言,它是C#开发中不可或缺的一部分,尤其是在数据交换、配置文件和序列化等方面。本教程将深入探讨C#编程人员如何掌握XML的基本概念和实用技巧。 首先,...
在C#编程中,工具类库是...总的来说,这个C#工具类库提供了全面的辅助功能,覆盖了开发过程中的许多常见需求,是一个强大的开发资源。通过合理利用这些工具,开发者可以更专注于业务逻辑,提高代码的可维护性和效率。