用xmldocument递归索引XML
本文关键字:XML 索引 递归 xmldocument | 更新日期: 2023-09-27 18:05:50
我试图在c#中使用递归添加"索引"属性到所有XML节点。我的问题是,当我试图将属性添加到没有子节点的节点时,它会以空引用失败。例如,对于一个简单的XML文件(html),我希望它像这样:
<div index="1">
<div index="1.1">
<h2 index="1.1.1">some text1</h2>
<h2 index="1.1.2">some text</h2>
</div>
</div>
<div index="2">
<table index="2.1">
<tr index="2.1.1">
<td index="2.1.1.1">some cell</td>
<td index="2.1.1.2">some cell</td>
</tr>
<tr index="2.1.2">
<td index="2.1.2.1">some cell</td>
</tr>
</table>
</div>
<div index="3">
<h1 index="3.1">some text</h1>
</div>
我的函数现在看起来像这样:
public static string TraverseNodes(XmlNode node,XmlDocument xmlDoc,bool isChild)
{
int i = 1;
foreach (XmlNode subNode in node)
{
var child = subNode.ChildNodes[i];
if (subNode.ChildNodes[i] != null)
{
XmlAttribute typeAttr = xmlDoc.CreateAttribute("realIndex");
typeAttr.Value = (isChild ? (i+ ".") : "") + (i + 1);
subNode.Attributes.Append(typeAttr);
}
i++;
TraverseNodes(subNode, xmlDoc, isChild);
}
return PrintXml(xmlDoc);
}
也许我的整个方法是错误的。如有任何帮助我都很高兴。
我将使用LINQ to XML。我的第一个有点低效的方法是:
foreach (var element in doc.Descendants())
{
int indexInLevel = element.ElementsBeforeSelf().Count() + 1;
var parent = element.Parent;
string prefix = parent == null ? "" : (string) parent.Attribute("index") + ".";
element.SetAttributeValue("index", prefix + indexInLevel);
}
注意,这将使根元素的索引为"1"。它依赖于Descendants
按文档顺序遍历的事实,因此在遍历到子元素之前,任何元素的父元素都已经设置了index属性。
现在我说过这是相当低效的,因为它每次都需要计算所有较早的兄弟节点。您可以使用递归使其更有效,并且也更灵活一些:
public void AssignIndexes(XElement element, string prefix, int index)
{
string value = prefix + index;
element.SetAttributeValue("index", value);
value += "."; // As the prefix for all children
int subindex = 1;
foreach (var child in element.Elements())
{
AssignIndexes(child, value, subindex++);
}
}
public void AssignIndexesToChildren(XElement element)
{
int subindex = 1;
foreach (var child in element.Elements())
{
AssignIndexes(child, "", subindex++);
}
}
现在您可以调用AssignIndexesToChildren(doc.Root)
,它将忽略根元素,但为第一个子元素创建"1",等等。
我使用的是XML linq,但是代码可以直接转换为XML
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string input =
"<html>" +
"<body>" +
"<div>" +
"<div>" +
"<h2>some text1</h2>" +
"<h2>some text</h2>" +
"</div>" +
"</div>" +
"<div>" +
"<table>" +
"<tr>" +
"<td>some cell</td>" +
"<td>some cell</td>" +
"</tr>" +
"<tr>" +
"<td>some cell</td>" +
"</tr>" +
"</table>" +
"</div>" +
"<div>" +
"<h1>some text</h1>" +
"</div>" +
"</body>" +
"</html>";
XDocument doc = XDocument.Parse(input);
XElement body = doc.Descendants("body").FirstOrDefault();
List<int> indexes = new List<int>();
AddIndex(body, indexes);
}
static void AddIndex(XElement elements, List<int> indexes)
{
indexes.Add(0);
foreach (XElement element in elements.Elements())
{
indexes[indexes.Count - 1] += 1;
element.Add(new XAttribute("index", string.Join(".",indexes.Select(x => x.ToString()))));
if (element.HasElements)
{
AddIndex(element, indexes);
}
}
indexes.RemoveAt(indexes.Count - 1);
}
}
}
ok,这是一个选项。你可以用
AssignIndex(myXmlDoc.DocumentElement,0,0)
和它应该工作(未测试);
public static string AssignIndex(XmlNode node, int nodeIdx, int childIdx)
{
if (childIdx != 0) {
XmlAttribute typeAttr = xmlDoc.CreateAttribute("realIndex");
typeAttr.Value = (nodeIdx == 0 ? "": (nodeIdx+ ".")) + childIdx;
node.Attributes.Append(typeAttr);
}
int i=1;
foreach (XmlNode subNode in node.ChildNodes)
{
AssignIndex(subNode, childIdx, i++);
}
}