using System; using System.IO; using System.Xml; namespace LunarSF.SHomeWorkshop.LunarMarkdownEditor { /// <summary> /// [静态]Xml功能类。 /// 创建时间:2011年12月26日 /// 创建者:杨震宇 /// /// 主要内容:为XmlNode添加了几个扩展方法,以便操作Xml文件。 /// </summary> public static class XmlTools { /// <summary> /// [扩展方法]将一串Xml格式的文本转换成XmlNode节点,并插入为此节点的子节点。 /// /// 异常: /// ArgumentNullException /// </summary> /// <param name="xmlNode">调用本方法的Xml节点(本节点将成为新节点的父节点)。</param> /// <param name="xmlText">需要转换成新子节点的Xml文本(需要符合Xml格式)。</param> /// <returns>如果成功转换并插入到当前节点尾部,成为当前节点的最后一个子节点,就返回【新子节点】。失败则返回null。</returns> public static XmlNode AppendXmlAsChild(this XmlNode xmlNode, String xmlText) { if (xmlText == null || xmlText.Length <= 0) throw new ArgumentNullException("XmlNode的AppendXmlAsChild()扩展方法的“xmlText”参数值不能为null或空字符串!"); XmlDocument doc = new XmlDocument(); doc.LoadXml(xmlText); XmlNode newNode = xmlNode.OwnerDocument.CreateNode (doc.FirstChild.NodeType, doc.FirstChild.Name, doc.FirstChild.NamespaceURI); foreach (XmlAttribute attribute in doc.FirstChild.Attributes) { XmlAttribute newAttribute = xmlNode.OwnerDocument.CreateAttribute(attribute.Name); newAttribute.Value = attribute.Value; newNode.Attributes.Append(newAttribute); } newNode.InnerXml = doc.FirstChild.InnerXml; xmlNode.AppendChild(newNode); return newNode; } public static XmlElement AppendXmlAsChild(this XmlElement xmlElement, String xmlText) { if (xmlText == null || xmlText.Length <= 0) throw new ArgumentNullException("XmlNode的AppendXmlAsChild()扩展方法的“xmlText”参数值不能为null或空字符串!"); XmlDocument doc = new XmlDocument(); doc.LoadXml(xmlText); XmlElement newElement = xmlElement.OwnerDocument.CreateElement(doc.FirstChild.Name, doc.FirstChild.NamespaceURI); foreach (XmlAttribute attribute in doc.FirstChild.Attributes) { XmlAttribute newAttribute = xmlElement.OwnerDocument.CreateAttribute(attribute.Name); newAttribute.Value = attribute.Value; newElement.Attributes.Append(newAttribute); } newElement.InnerXml = doc.FirstChild.InnerXml; xmlElement.AppendChild(newElement); return newElement; } /// <summary> /// [扩展方法]取出当前节点XmlNode中指定名称的属性。如果指定的属性不存在,会返回null。 /// </summary> /// <param name="xmlNode">要取属性的节点。</param> /// <param name="attributeName">要取的属性的名称。</param> /// <returns>返回指定名称的属性。如果不存在,返回null。</returns> public static XmlAttribute GetAttribute(this XmlNode xmlNode, String attributeName) { if (xmlNode == null) return null; if (xmlNode.Attributes == null) return null; XmlAttributeCollection attributes = xmlNode.Attributes; foreach (XmlAttribute attribute in attributes) { if (attribute.Name == attributeName) { return attribute; } } return null; } /// <summary> /// [扩展方法]取XmlElement的属性。如果存在指定名称的属性(Attribute), /// 返回此属性(Attribute)。否则返回null。 /// </summary> /// <param name="xmlElement">要调用本扩展方法的XmlElement元素自身。</param> /// <param name="attributeName">要删除的属性的名称。</param> /// <returns>返回属性。</returns> public static XmlAttribute GetAttributeByName(this XmlElement xmlElement, String attributeName) { if (xmlElement.Attributes == null) return null; XmlAttributeCollection attributes = xmlElement.Attributes; foreach (XmlAttribute attribute in attributes) { if (attribute.Name == attributeName) { return attribute; } } return null; } /// <summary> /// [扩展方法]取出节点中指定名称的属性的文本值。如果不存在,返回null,而不是返回空字符串。 /// </summary> /// <param name="xmlNode">调用本方法的XmlNode节点(取其属性值的节点)。</param> /// <param name="attributeName">要取值的属性的名称。</param> /// <returns>返回属性的值(文本值)。如果不存在该属性,返回null。</returns> public static String GetAttributeValueText(this XmlNode xmlNode, String attributeName) { XmlAttributeCollection attributes = xmlNode.Attributes; foreach (XmlAttribute attribute in attributes) { if (attribute.Name == attributeName) { return attribute.InnerText; } } return null; } /// <summary> /// [扩展方法]取本节点的子节点列表中指定的某个子节点的索引值。 /// 如果指定的节点不是当前节点的子节点,返回-1。 /// /// 异常: /// Exception /// </summary> /// <param name="xmlNode">调用本方法的节点。</param> /// <param name="childNode">应为调用节点的子节点。返回值将是这个子节点的索引。</param> /// <returns>如果指定子节点不是调用此方法的节点的子节点,返回-1。如果找到,则返回其索引。</returns> public static int IndexOf(this XmlNode xmlNode, XmlNode childNode) { if (childNode == null) return -1; XmlNode parentNode = xmlNode; if (parentNode == null) throw new Exception("XmlNode的IndexOf()扩展方法在执行时未找到父节点。"); int curIndex = -1; for (int i = 0; i < parentNode.ChildNodes.Count; i++) { XmlNode node = parentNode.ChildNodes[i]; if (node == childNode) { curIndex = i; break; } } return curIndex; } /// <summary> /// [扩展方法]在当前节点的子节点列表中,按指定索引插入一个新的XmlNode。 /// 如果插入成功,返回true。 /// /// 异常: /// IndexOutOfRangeException /// </summary> /// <param name="xmlNode">调用本方法的节点(新节点将成为此节点的子节点)。</param> /// <param name="index">新节点应插入到的位置。</param> /// <param name="newXmlChildNode">新子节点。</param> /// <returns>如果插入成功,返回true。</returns> public static bool InsertChildNodeAt(this XmlNode xmlNode, int index, XmlNode newXmlChildNode) { if (xmlNode.OwnerDocument == null) throw new Exception("节点不在文档中!"); if (xmlNode.OwnerDocument.PreserveWhitespace) throw new Exception("Xml文档的“PreserveWhitespace”属性值不应为true。"); if (index < 0) throw new IndexOutOfRangeException("XmlNode的InsertChildNodeAt()扩展方法的“index”参数值不能小于0!"); if (index == 0) { if (xmlNode.FirstChild != null) { xmlNode.InsertBefore(newXmlChildNode, xmlNode.FirstChild); return true; } else { xmlNode.AppendChild(newXmlChildNode); return true; } } if (index == xmlNode.ChildNodes.Count) { xmlNode.AppendChild(newXmlChildNode); return true; } XmlNodeList childNodeList = xmlNode.ChildNodes; if (index > childNodeList.Count) throw new IndexOutOfRangeException("XmlNode的InsertChildNodeAt()扩展方法的“index”参数值不能大于子节点总数目!"); return (xmlNode.InsertAfter(newXmlChildNode, childNodeList[index - 1]) != null); } /// <summary> /// [扩展方法]将提供的Xml文本转换成一个XmlNode并插入到自身的后面或前面。 /// 新节点与当前调用本方法的节点平级。 /// 注意:只能传入一个节点的OutXmlText,不能同时传入多个节点的。 /// </summary> /// <param name="xmlNode">调用本方法的节点。(新节点将与本节点平级。)</param> /// <param name="xmlText">新节点的OutXmlText文本。</param> /// <param name="afterMe">默认为:true,表示在本节点之后;false表示在本节点之前插入。</param> /// <returns>如果插入成功,返回true。</returns> public static bool InsertXml(this XmlNode xmlNode, String xmlText, bool afterMe = true) { XmlNode parentNode = xmlNode.ParentNode; XmlDocument doc = new XmlDocument(); doc.LoadXml(xmlText); XmlNode newNode = xmlNode.OwnerDocument.CreateNode (doc.FirstChild.NodeType, doc.FirstChild.Name, doc.FirstChild.NamespaceURI); foreach (XmlAttribute attribute in doc.FirstChild.Attributes) { XmlAttribute newAttribute = xmlNode.OwnerDocument.CreateAttribute(attribute.Name); newAttribute.Value = attribute.Value; newNode.Attributes.Append(newAttribute); } newNode.InnerXml = doc.FirstChild.InnerXml; if (afterMe) { parentNode.InsertAfter(newNode, xmlNode); } else { parentNode.InsertBefore(newNode, xmlNode); } return true; } /// <summary> /// [扩展方法]在指定索引处插入新的Xml节点。新节点将成为子节点。返回子节点。 /// /// 异常: /// IndexOutOfRangeException /// ArgumentNullException /// </summary> /// <param name="xmlNode">调用本方法的XmlNode(将成为新节点的父节点)。</param> /// <param name="xmlText">新子节点的OutXml文本。</param> /// <param name="index">新子节点应出现在父节点中哪个位置,索引值从0开始计算。</param> /// <returns>返回子节点。</returns> public static XmlNode InsertXmlAt(this XmlNode xmlNode, String xmlText, int index) { if (index < 0) throw new IndexOutOfRangeException("XmlNode的InsertXmlAt()扩展方法的“index”参数值不能小于0!"); if (xmlText == null || xmlText.Length <= 0) throw new ArgumentNullException("XmlNode的InsertXmlAt()扩展方法的“xmlText”参数不能为null或空字符串!"); if (index > xmlNode.ChildNodes.Count) throw new IndexOutOfRangeException("XmlNode的InsertXmlAt()扩展方法的“index”参数值不能大于子节点最大索引!"); XmlDocument doc = new XmlDocument(); doc.LoadXml(xmlText); XmlNode newNode = xmlNode.OwnerDocument.CreateNode (doc.FirstChild.NodeType, doc.FirstChild.Name, doc.FirstChild.NamespaceURI); foreach (XmlAttribute attribute in doc.FirstChild.Attributes) { XmlAttribute newAttribute = xmlNode.OwnerDocument.CreateAttribute(attribute.Name); newAttribute.Value = attribute.Value; newNode.Attributes.Append(newAttribute); } newNode.InnerXml = doc.FirstChild.InnerXml; if (index > 0) { XmlNode preNode = xmlNode.ChildNodes[index - 1]; if (preNode == null) return null; return xmlNode.InsertAfter(newNode, preNode); } else { return xmlNode.InsertBefore(newNode, xmlNode.FirstChild); } } /// <summary> /// [静态方法]从磁盘上读取Xml文件,如果正常,返回一个XmlDocument对象。 /// /// 异常: /// ArgumentNullException /// FileNotFoundException /// </summary> /// <param name="fileFullPath">Xml文件在磁盘上的完整路径。</param> /// <returns>如果正常,返回一个XmlDocument对象。</returns> public static XmlDocument LoadXmlFile(string fileFullPath) { if (fileFullPath == null || fileFullPath.Length <= 0) throw new ArgumentNullException("XmlTools.LocaXmlFile()方法的“fileFullPath”参数值不能为null或空字符串!"); if (System.IO.File.Exists(fileFullPath) == false) throw new FileNotFoundException("XmlTools.LocaXmlFile()方法未能找到文件:" + fileFullPath); XmlReader reader = null; try { XmlReaderSettings settings = new XmlReaderSettings(); settings.ConformanceLevel = ConformanceLevel.Document; settings.IgnoreWhitespace = true; settings.IgnoreComments = true; string uriStr = fileFullPath; //fileFullPath.StartsWith("file:///") ? fileFullPath : "file:///" + fileFullPath; reader = XmlReader.Create(uriStr, settings); XmlDocument xmlDoc = new XmlDocument(); xmlDoc.RemoveAll(); System.Xml.XmlNode node = xmlDoc.ReadNode(reader); ; while (node != null) { xmlDoc.AppendChild(node); node = xmlDoc.ReadNode(reader); } return xmlDoc; } finally { if (reader != null) reader.Close(); } } /// <summary> /// [扩展方法]将当前节点移动到同级节点中的指定索引位置。如果当前位置即是指定位置,也返回true。 /// 如果此节点没有父节点,则不能移动。如果指定索引值越界,也不能移动。 /// /// 异常: /// Exception /// IndexOutOfRangeException /// </summary> /// <param name="xmlNode">调用本方法的当前节点。</param> /// <param name="newIndex">当前节点的新索引。必须介于[0,同级节点数目-1]之间。</param> /// <returns>如果成功返回true。</returns> public static bool MoveTo(this XmlNode xmlNode, int newIndex) { if (newIndex < 0) throw new IndexOutOfRangeException("XmlNode的扩展方法MoveTo()的“newIndex”参数值不能小于零!"); XmlNode parentNode = xmlNode.ParentNode; if (parentNode == null) throw new Exception("XmlNode的扩展方法MoveTo()在执行时未找到“parentNode”,无法继续。"); if (newIndex >= parentNode.ChildNodes.Count) throw new IndexOutOfRangeException("XmlNode的扩展方法MoveTo()的“newIndex”参数值不能大于同级节点最大索引!"); int curIndex = parentNode.IndexOf(xmlNode); if (curIndex < 0) throw new Exception("发生意外,未找到当前节点在同级节点列表中的索引。XmlNode的IndexOf()扩展方法执行错误。"); if (newIndex < curIndex) { parentNode.RemoveChild(xmlNode); parentNode.InsertBefore(xmlNode, parentNode.ChildNodes[newIndex]); return true; } else if (newIndex > curIndex) { XmlNode baseDestNode = parentNode.ChildNodes[newIndex]; if (baseDestNode == null) throw new IndexOutOfRangeException("XmlNode的扩展方法MoveTo()执行“向后移动”时未能找到“newIndex”位置的节点!"); parentNode.RemoveChild(xmlNode); parentNode.InsertAfter(xmlNode, baseDestNode); return true; } else return true; } /// <summary> /// [扩展方法]将调用本方法的节点与同级节点中的前一个节点对调位置。 /// 如果此节点本身已经是同级节点中的首位,则不进行任何操作并返回false。 /// /// 异常: /// Exception /// </summary> /// <param name="xmlNode">调用本方法的节点本身。</param> /// <returns>如果找不到父节点,或本身就是首节点,返回false。成功后返回true。</returns> public static bool MoveToPrevious(this XmlNode xmlNode) { XmlNode previousNode = xmlNode.PreviousSibling; if (previousNode == null) return false; XmlNode parentNode = xmlNode.ParentNode; if (parentNode == null) throw new Exception("XmlNode的MoveToPrevious()扩展方法在执行时未找到父节点。"); parentNode.RemoveChild(xmlNode); parentNode.InsertBefore(xmlNode, previousNode); return true; } /// <summary> /// [扩展方法]将调用本方法的节点与同级节点中的后一个节点对调位置。 /// 如果此节点本身已经是同级节点中的最后一个节点,则不进行任何操作并返回false。 /// /// 异常: /// Exception /// </summary> /// <param name="xmlNode">调用本方法的节点本身。</param> /// <returns>如果找不到父节点,或本身就是最后一个节点,返回false。成功后返回true。</returns> public static bool MoveToNext(this XmlNode xmlNode) { XmlNode nextNode = xmlNode.NextSibling; if (nextNode == null) return false; XmlNode parentNode = xmlNode.ParentNode; if (parentNode == null) throw new Exception("XmlNode的MoveToNext()扩展方法在执行时未找到父节点。"); parentNode.RemoveChild(xmlNode); parentNode.InsertAfter(xmlNode, nextNode); return true; } /// <summary> /// [扩展方法]删除此节点的所有子节点(node)。但不删除Attributes。 /// </summary> /// <param name="xmlElement">调用此扩展方法的XmlNode实例本身。</param> public static void RemoveAllChildNodes(this XmlNode xmlNode) { if (xmlNode == null) return; if (xmlNode.ChildNodes.Count <= 0) return; for (int i = xmlNode.ChildNodes.Count - 1; i >= 0; i--) { xmlNode.RemoveChild(xmlNode.ChildNodes[i]); } } /// <summary> /// [扩展方法]移除指定名称的属性。 /// </summary> /// <param name="xmlNode">节点。</param> /// <param name="attributeName">属性名。</param> /// <returns>是否移除成功。</returns> public static bool RemoveAttributeByName(this XmlNode xmlNode, string attributeName) { if (xmlNode == null) return false; if (xmlNode.Attributes == null) return false; XmlAttributeCollection attributes = xmlNode.Attributes; foreach (XmlAttribute attribute in attributes) { if (attribute.Name == attributeName) { xmlNode.Attributes.Remove(attribute); return true; } } return false; } /// <summary> /// [扩展方法]替换当前节点的OutXmlText(此节点的所有内容:名称、特性、子节点等等均将由提供的Xml字符串所决定。 /// 返回替换后的新节点。 /// /// 异常: /// ArgumentNullException /// </summary> /// <param name="xmlNode">调用本方法的(要替换的)节点。</param> /// <param name="newOutXmlText">节点的新OutXml文本。</param> /// <returns>返回被替换后的新节点。</returns> public static XmlNode ReplaceOutXmlText(this XmlNode xmlNode, string xmlText) { if (xmlText == null || xmlText.Length <= 0) throw new ArgumentNullException("XmlNode的ReplaceOutXmlText()扩展方法的“XmlText”参数不能为null或空字符串!"); XmlNode parentNode = xmlNode.ParentNode; XmlDocument doc = new XmlDocument(); //Load方法不需要XmlHeader!!! //if (xmlText.StartsWith("<xml") == false) //{ // xmlText = xmlHeaderText + xmlText; //} doc.LoadXml(xmlText); XmlNode newNode = xmlNode.OwnerDocument.CreateNode (doc.FirstChild.NodeType, doc.FirstChild.Name, doc.FirstChild.NamespaceURI); if (doc.FirstChild.Attributes != null && doc.FirstChild.Attributes.Count > 0) { foreach (XmlAttribute attribute in doc.FirstChild.Attributes) { XmlAttribute newAttribute = xmlNode.OwnerDocument.CreateAttribute(attribute.Name); newAttribute.Value = attribute.Value; newNode.Attributes.Append(newAttribute); } } newNode.InnerXml = doc.FirstChild.InnerXml; if (parentNode.ReplaceChild(newNode, xmlNode) != null) { return newNode; } else return null; } /// <summary> /// [静态方法]将文本中的小于号和&符转换成<和&以防止造成Xml格式失效。 /// </summary> /// <param name="source"></param> /// <returns></returns> public static string ReplaceXmlChars(string source) { if (source == null) return null; if (source.Length <= 0) return string.Empty; //Xml预定的转义字符有五个,只有“<”和“&”是严格禁止使用的。 string result = source.Replace("&", "&"); result = result.Replace("<", "<"); result = result.Replace(">", ">"); result = result.Replace("'", "'"); result = result.Replace("\"", """); return result; } /// <summary> /// [静态方法]将Xml文本中的<和&转换回小于号和&符。 /// </summary> /// <param name="source"></param> /// <returns></returns> public static string RestoreXmlChars(string source) { if (source == null) return null; if (source.Length <= 0) return string.Empty; //Xml预定的转义字符有五个,只有“<”和“&”是严格禁止使用的。 string result = source.Replace(""", "\""); result = result.Replace("'", "'"); result = result.Replace(">", ">"); result = result.Replace("<", "<"); result = result.Replace("&", "&"); return result; } /// <summary> /// [静态方法]将Xml序列字符串保存为一个磁盘上的Xml文件。 /// ★注意:XmlDocument.LoadXml()方法传入的文本不需要以Xml声明文本开头!!!不必重复检查!!! /// /// 异常: /// ArgumentException /// ArgumentNullException /// </summary> /// <param name="fullPathOfXmlFile">文件路径。</param> /// <param name="xmlString">Xml字符串。</param> /// <returns>返回保存后的XmlDocument对象。</returns> public static XmlDocument SaveToXmlFile(string fullPathOfXmlFile, string xmlString) { if (fullPathOfXmlFile == null || fullPathOfXmlFile.Length <= 0) throw new ArgumentNullException("XmlNode的SaveToXmlFile()扩展方法的“fullPathOfXmlFile”参数值不能为null或空字符串!"); if (xmlString == null || xmlString.Length <= 0) throw new ArgumentNullException("XmlNode的SaveToXmlFile()扩展方法的“xmlString”参数值不能为null或空字符串!"); //if (xmlString.StartsWith("<?xml") == false) //{ // xmlString = XmlTools.XmlHeaderText + xmlString; //} //LoadXml方法不需要XmlHeader。 XmlDocument doc = new XmlDocument(); doc.LoadXml(xmlString); string directoryString; int indexOfLastSeparator = fullPathOfXmlFile.LastIndexOf("\\"); if (indexOfLastSeparator == -1) { throw new ArgumentException("XmlTools.SaveToXmlFile()方法的“fullPathOfXmlFile”参数有误,未找到目录分割符“\\”。"); } directoryString = fullPathOfXmlFile.Substring(0, indexOfLastSeparator); if (Directory.Exists(directoryString) == false) { Directory.CreateDirectory(directoryString); if (Directory.Exists(directoryString) == false) return null;//未能创建目录。 } doc.Save(fullPathOfXmlFile); return doc; } /// <summary> /// [扩展方法]为节点设置一个属性(Attribute)值。如果已存在该属性,则更新其值;若尚无该属性,则添加一个新属性。 /// /// 异常: /// ArgumentNullException /// </summary> /// <param name="xmlNode">调用本方法(要设置属性值)的节点。</param> /// <param name="attributeName">属性名。不能是null或空字符串。</param> /// <param name="attributeValue">属性值。不能为null,但可以是空字符串。</param> public static void SetAttribute(this XmlNode xmlNode, string attributeName, string attributeValue) { if (attributeName == null || attributeName.Length <= 0) throw new ArgumentNullException("XmlNode的SetAttribute()扩展方法的“attributeName”参数值不能是null或空字符串!"); if (attributeValue == null) throw new ArgumentNullException("XmlNode的SetAttribute()扩展方法的“attributeValue”参数值不能是null!"); XmlAttributeCollection attributes = xmlNode.Attributes; XmlAttribute attribute = null; foreach (XmlAttribute a in attributes) { if (a.Name == attributeName) { attribute = a; break; } } if (attribute == null) { XmlAttribute newAttribute = xmlNode.OwnerDocument.CreateAttribute(attributeName); newAttribute.InnerText = attributeValue; xmlNode.Attributes.Append(newAttribute); } else { if (attribute.InnerText != attributeValue) { attribute.InnerText = attributeValue; } } } public static void SetAttribute(this XmlElement xmlElement, string attributeName, string attributeValue) { if (attributeName == null || attributeName.Length <= 0) throw new ArgumentNullException("XmlElement的SetAttribute()扩展方法的“attributeName”参数值不能是null或空字符串!"); if (attributeValue == null) throw new ArgumentNullException("XmlElement的SetAttribute()扩展方法的“attributeValue”参数值不能是null!"); XmlAttributeCollection attributes = xmlElement.Attributes; XmlAttribute attribute = null; foreach (XmlAttribute a in attributes) { if (a.Name == attributeName) { attribute = a; break; } } if (attribute == null) { XmlAttribute newAttribute = xmlElement.OwnerDocument.CreateAttribute(attributeName); newAttribute.InnerText = attributeValue; xmlElement.Attributes.Append(newAttribute); } else { if (attribute.InnerText != attributeValue) { attribute.InnerText = attributeValue; } } } private static readonly string xmlHeaderText = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"; /// <summary> /// [只读]返回Xml文件头一行声明字符串:"<?xml version=\"1.0\" encoding=\"utf-8\"?>"; /// </summary> public static string XmlHeaderText { get { return xmlHeaderText; } } } }