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文件读取工具类,涉及C#针对XML文件各节点获取的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下
《C#全能速查宝典》共分为8章,分别介绍了C#语言基础、Windows窗体及常用控件、Windows高级控件、控件公共属性、方法及事件、数据库开发、文件、数据流与注册表、GDI+绘图技术和C#高级编程,共包含562个C#编程中常用...
Unity下基于C#的XML工具类,可实现XML的增删改查,Android移动端读写
40. 共用工具类 41. 其他 42. 分词辅助类 43. 分页 44. 加密解密 45. 压缩解压缩 46. 各种验证帮助类 47. 图片 48. 图片操作类 49. 图片验证码 50. 处理多媒体的公共类 51. 处理枚举类 52. 字符串 53. ...
Unity3D_XMLToEgg 基于C#编写的Unity3D工具,利用反射和泛型通过读取XML文件创建新的实例并且为实例赋值。 但是我更喜欢Unity3D_XML2O这个名字,O意味着object也象征着鸡蛋。 XMLToEgg or called XML2O can convert ...
,RDLC直接打印帮助类,ResourceManager,RMB,SqlHelper,SQL语句拦截器,URL的操作类,VerifyCode,XML操作类,上传下载,二维码操作类,共用工具类,其他,分词辅助类,分页 ,加密解密,压缩解压缩,各种验证帮助类,图片,图片操作...
200多个工具类 .cs 文件读写 文件夹读写 各种加密解密 XML操作 #region 获得用户IP /// /// 获得用户IP /// public static string GetUserIp() { string ip; string[] temp; bool isErr = false; if ...
实例107 使用哈希表对XML文件进行查询 127 实例108 计算两个矩形矩阵的乘积 129 第5章 面向对象编程技术 131 5.1 属性和方法 132 实例109 使用属性存储用户编号和姓名 132 实例110 通过定义方法求一个数的平方 133 ...
6.1 用流读写文件 137 6.2 File类和FileInfo类 138 6.3 Directory类和DirectoryInfo类 140 6.4 例子:查找文件 143 6.5 例子:拆分和合并文件 144 习题: 145 第七章 多线程程序设计 146 7.1 线程类(Thread)的属性和...
│ │ ├─实例118 如何读写XML文件 │ │ ├─实例120 如何取得聚合函数返回值 │ │ ├─实例121 如何直接操作主从关系表 │ │ ├─实例122 如何以Web方式查询Access数据库 │ │ ├─实例123 如何使用列表视图...
AccessXML 演示如何读写XML文件数据 第17章(\ch17) 示例描述:介绍.NET进行TCP/IP网络通讯应用程序开发。 SocketListen 演示Socket进行服务端开发实例 SimpleIMServer SimpleIM软件服务器端实例 第18章(\ch...
│ ├─实例115 如何创建SQL Server数据库 │ │ ├─实例116 如何读写SQL Server数据库 │ │ ├─实例117 如何更新SQL Server数据库 │ │ ├─实例118 如何读写XML文件 │ │ ├─实例120 如何取得...
│ ├─实例115 如何创建SQL Server数据库 │ │ ├─实例116 如何读写SQL Server数据库 │ │ ├─实例117 如何更新SQL Server数据库 │ │ ├─实例118 如何读写XML文件 │ │ ├─实例120 如何取得...