用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);
        }

也许我的整个方法是错误的。如有任何帮助我都很高兴。

用xmldocument递归索引XML

我将使用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++);
        }
    }