如何在C#中按节点属性对XML文件进行排序

3

我不需要任何人为我编写此解决方案 - 只是希望能得到有关最佳方法的指导。我正在使用C#代码在VS2015中处理.aspx文件。

我找到了无数的线程来解释如何排序XML文件中的节点。但是,我没有找到任何关于如何根据共同的子节点属性对具有相同结构的多个XML文件进行排序的线程。

我的情况:我有一个包含数百个XML文件的目录,文件名分别为0001.xml到6400.xml。每个XML文件具有相同的结构。我想根据子节点的属性对文件进行排序(而不是节点)。

每个XML文件都有一个“item”父节点,并且具有子节点“year”,“language”和“author”,以及其他节点。例如:

<item id="0001">
   <year>2011</year>
   <language id="English" />
   <author sortby="Smith">John F. Smith</author>
   <content></content>
</item>

如果我想按照item/author节点的@sortby属性的字母顺序而不是按顺序0001到6400列出文件,该怎么办?

我有一个想法,那就是创建一个临时XML文件,从每个XML文件中收集所需信息。然后,我可以对临时XML文件进行排序,然后循环遍历节点以按正确顺序显示文件。类似这样...

XDocument tempXML = new XDocument();
// add parent node of <items>

string[] items = Directory.GetFiles(directory)
foreach (string item in items)
{
   // add child node of <item> with attributes "filename", "year", "language", and "author"
}

// then sort the XML nodes according to attributes

这有意义吗?有没有更聪明的方法来做到这一点?


谢谢大家。在很短的时间内,这里有很多答案,使用了一些我熟悉的语法和一些我不熟悉的语法。现在正在努力理解... - TJM
@Tjm,你看到我的回答了吗?为什么你接受了其他人的回答?他抄袭了我的回答。这不公平。 - Alberto Monteiro
声誉、标签徽章和WinForms、C#、VB.NET中的标签顶级用户。行动胜于言辞。 - Reza Aghaei
选择接受的答案是楼主的权利 :) 提出无根据的主张是不礼貌的,但确认这种行为更具破坏性。 - Reza Aghaei
1
我在这个论坛上还是一个新手,也不经常使用,所以我还在学习这个论坛的规范。我感谢大家的反馈,并已经给多个答案点赞了。 - TJM
显示剩余3条评论
5个回答

4

排序

我们可以使用一点LINQ to Xml展示已排序的xml文件,代码如下:

var xmlsWithFileName = Directory.GetFiles(directory)
                                .Select(fileName => new { fileName, xml = XDocument.Parse(File.ReadAllText(fileName)) })
                                .OrderBy(tuple => tuple.xml.Element("item").Element("author").Attribute("sortby").Value);

xmlsWithFileName的每个元素都会有以下内容:

  • xml属性,其中包含XDocument中的XML
  • fileName属性,其中包含XML文件的路径

假设您的目标目录中有以下XML文件:

0001.xml

<item id="0001">
   <year>2011</year>
   <language id="English" />
   <author sortby="Smith">John F.Smith</author>
   <content></content>
</item>

0002.xml

<item id="0002">
   <year>2012</year>
   <language id="Portuguese" />
   <author sortby="Monteiro">Alberto Monteiro</author>
   <content></content>
</item>

您可以使用这段代码进行测试

public static void ShowXmlOrderedBySortByAttribute(string directory)
{
    var xmlsWithFileName = Directory.GetFiles(directory)
                                    .Select(fileName => new { fileName, xml = XDocument.Parse(File.ReadAllText(fileName)) })
                                    .OrderBy(tuple => tuple.xml.Element("item").Element("author").Attribute("sortby").Value);

    foreach (var xml in xmlsWithFileName)
    {
        Console.WriteLine($"Filename: {xml.fileName}{Environment.NewLine}Xml content:{Environment.NewLine}");
        Console.WriteLine(xml.xml.ToString());
        Console.WriteLine("================");
    }
}

这段代码的输出结果是:

Filename: c:\temp\teste\0002.xml
Xml content:

<item id="0002">
  <year>2012</year>
  <language id="Portuguese" />
  <author sortby="Monteiro">Alberto Monteiro</author>
  <content></content>
</item>
================
Filename: c:\temp\teste\0001.xml
Xml content:

<item id="0001">
  <year>2011</year>
  <language id="English" />
  <author sortby="Smith">John F.Smith</author>
  <content></content>
</item>
================

正如您所看到的,XML 0002.xml 出现在第一位置,然后是 0001.xml。


2

编辑:现在我想一想,您可能想要文件内容而不是文件名,如果是这样,您可以将此示例中的“items”数组替换为包含文件内容的字符串集合,并使用GetAuthor遍历该字符串并返回作者名称。

我认为最好的解决方案是将这些文件名添加到某种可排序的集合中。这将获取您的文件名并将它们添加到一个查找表中:

var lookup = items.ToLookup(a => GetAuthor(a)).OrderBy(a => a.Key);

这将依赖于一种使用文件名获取作者名称的方法:

private string GetAuthor(string filename)
{
    string author = String.Empty;

    // get author name logic

    return author;
}

最后,要遍历您的列表,请执行以下操作:
foreach (IGrouping<string, string> author in lookup)
{
    foreach (string file in author)
    {
        Console.WriteLine(String.Format("{0}: {1}", author.Key, file ));
    }
}

如果您决定按照多个标准对列表进行排序,您需要采取不同的方法创建一个自定义对象,将这些对象添加到列表中,并使用自定义的IComparer。但是,如果您只关心作者名称,那么本例将允许您避免所有这些麻烦。请注意保留HTML标签。

1

有两种方法可以按照XML文件节点的InnerText对数据进行排序

  1. 使用Linq 您可以将所有项目加载到列表中,然后按照子节点元素进行排序。 您可以编写一个只有一个参数是子节点名称的函数来完成此操作。
  2. 您可以使用XSLT进行转换

有关更多详细信息,请参考Sorting of XML file by XMLElement's InnerText

希望能够帮到您!


1
您可以使用XElement加载项目,并以此方式进行排序:
var items = System.IO.Directory.GetFiles(@"path", "*.xml")
                     .Select(file => System.Xml.Linq.XElement.Load(file));
                     .OrderBy(x => x.Element("author").Attribute("sortby").Value)
                     .ToList();

此外,如果您需要文件名,可以选择包含FileNameItem的对象。
var items = System.IO.Directory.GetFiles(@"path", "*.xml")
                     .Select(file => new
                     {
                         FileName = file, 
                         Item = System.Xml.Linq.XElement.Load(file)
                     })
                     .OrderBy(x => x.Item.Element("author").Attribute("sortby").Value)
                     .Select(x=>x.FileName) /*or .Select(x=>x.Item)*/
                     .ToList();

1

如果我正确理解了你的意思,我会这样去做:

SortedDictionary<string, string> dict = new SortedDictionary<string, string>();
var files = Directory.GetFiles(@"[path to files]", "*.xml");

foreach (var item in files)
{
    XDocument doc = XDocument.Load(item);
    var sortvalue = (from lv1 in doc.Descendants("somesortvalue")
                     select lv1.Value).First();

    dict.Add(sortvalue, item);
}

然后你可以使用dict.keys做一个foreach循环,这样文件名将按照字典的功能进行排序。

我进行了一些测试...使用了成千上万个随机文件名和随机排序值,遇到了一个问题...重复的键。所以...这是一个不完美的解决方案。 - Bubba

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