如何从XML文件中获取所有元素名称的C#方法

24

我想从一个 XML 文件中获取所有元素名称,例如以下 XML 文件:

<BookStore>
  <BookStoreInfo>
    <Address />
    <Tel />
    <Fax />
  <BookStoreInfo>
  <Book>
    <BookName />
    <ISBN />
    <PublishDate />
  </Book>
  <Book>
   ....
  </Book>
</BookStore>

我想获取元素"BookName"的名称。只需要"ISBN"和"PublishDate"这些名称,不包括"BookStoreInfo"及其子节点的名称。

我尝试了几种方法,但都没有成功,你们有什么建议吗?

7个回答

37

使用XDocument和LINQ-to-XML:

foreach(var name in doc.Root.DescendantNodes().OfType<XElement>()
        .Select(x => x.Name).Distinct())
{
    Console.WriteLine(name);
}

有很多类似的路由,但是。


非常感谢。我不熟悉LINQ-XML,但它运行得非常好。还有一件事,我更新了上面的问题。如何仅获取<BookStore>元素下的子节点名称。 - Smallville
如果您指的是“Book”(根据更新的问题),类似于:doc.Root.Element("Book").DescendantNodes()... - Marc Gravell
1
是的,那就是我的意思。代码也能正常运行,再次感谢您! - Smallville
@MarcGravell,我们能否使用这种方法获取整个节点集合,我的意思是具有特定名称的所有节点。 - Raghuveer
@Chintan_Patel在下面指出,您可以使用GetElementsByTagName方法在单行中完成它。 - Jansky

8

使用XPath

XmlDocument xdoc = new XmlDocument(); 
xdoc.Load(something);
XmlNodeList list = xdoc.SelectNodes("//BookStore");

该函数会返回文档中所有名为BookStore的节点列表。


5

我同意Adam的观点,理想情况是有一个定义XML文档内容的模式。然而,有时这是不可能的。以下是一种简单的方法,可以迭代XML文档的所有节点,并使用字典存储唯一的本地名称。我喜欢跟踪每个本地名称的深度,因此我使用int列表来存储深度。请注意,XmlReader“易于内存”,因为它不像XmlDocument那样加载整个文档。在某些情况下,这没有太大区别,因为xml数据的大小很小。在下面的示例中,使用XmlReader读取18.5MB文件。使用XmlDocument加载此数据将比使用XmlReader读取和采样其内容效率低。

string documentPath = @"C:\Docs\cim_schema_2.18.1-Final-XMLAll\all_classes.xml";

Dictionary<string, List<int>> nodeTable = new Dictionary<string, List<int>>();
using (XmlReader reader = XmlReader.Create(documentPath))
{
    while (!reader.EOF)
    {
        if (reader.NodeType == XmlNodeType.Element)
        {
            if (!nodeTable.ContainsKey(reader.LocalName))
            {
                nodeTable.Add(reader.LocalName, new List<int>(new int[] { reader.Depth }));
            }
            else if (!nodeTable[reader.LocalName].Contains(reader.Depth))
            {
                nodeTable[reader.LocalName].Add(reader.Depth);
            }
        }
        reader.Read();
    }
}
Console.WriteLine("The node table has {0} items.",nodeTable.Count);
foreach (KeyValuePair<string, List<int>> kv in nodeTable)
{
    Console.WriteLine("{0} [{1}]",kv.Key, kv.Value.Count);
    for (int i = 0; i < kv.Value.Count; i++)
    {
        if (i < kv.Value.Count-1)
        {
            Console.Write("{0}, ", kv.Value[i]);
        }
        else
        {
            Console.WriteLine(kv.Value[i]);
        }
    }
}

4

做这件事的纯理论方式(实际上也是正确的方式)是拥有一个架构契约定义,并以那种方式进行读取。话虽如此,你可以像这样做...

List<string> nodeNames = new List<string>();

foreach(System.Xml.XmlNode node in doc.SelectNodes("BookStore/Book"))
{
    foreach(System.Xml.XmlNode child in node.Children) 
    {
        if(!nodeNames.Contains(child.Name)) nodeNames.Add(child.Name);
    }
}

诚然,这是一种获取Book节点子级的不同节点名称列表的基本方法,但您没有在环境方面提供更多的规定(如果您使用3.5版本,可以使用LINQ to XML使其更美观)。无论如何,这应该可以完成工作。


3
如果您正在使用C# 3.0,可以执行以下操作:
var data = XElement.Load("c:/test.xml"); // change this to reflect location of your xml file

var allElementNames = 
    (from e in in data.Descendants()
    select e.Name).Distinct();

没有所谓的C# 3.5;它使用的是C# 3.0和.NET 3.5。 - Marc Gravell
很棒的答案。你如何计算每个标记出现的次数? - fab

2
如果您的根元素是 BookStore,那么您可以尝试以下代码。
XmlDocument doc = new XmlDocument();
        doc.Load(configPath);
        XmlNodeList list = doc.DocumentElement.GetElementsByTagName("Book");
        if (list.Count != 0)
        {
            for (int i = 0; i < list[0].ChildNodes.Count; i++)
            {
                XmlNode child = list[0].ChildNodes[i];

            }
        }

2
你可以尝试使用 XPATH 来执行它。
XmlDocument doc = new XmlDocument();
doc.LoadXml("xml string");

XmlNodeList list = doc.SelectNodes("//BookStore/Book");

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接