Linq到XML Group然后是OrderBy

时间:2016-01-14 10:53:28

标签: c# xml linq

我有点卡住,不能直接思考,也许你能帮助我。

我的.xml看起来像这样:

<RSA>
  <Size>
    <Name>0005-24</Name>
    <IDs>
      <ID>0003</ID>
      <ID>0004</ID>
      <ID>0005</ID>
      <ID>0006</ID>
      <ID>0007</ID>
      <ID>0008</ID>
      <ID>0010</ID>
      <ID>0009</ID>
    </IDs>
  </Size>
  <Size>
    <Name>0015-24</Name>
    <IDs>
      <ID>0003</ID>
      <ID>0004</ID>
      <ID>0005</ID>
      <ID>0006</ID>
      <ID>0007</ID>
      <ID>0008</ID>
      <ID>0010</ID>
      <ID>0009</ID>
    </IDs>
  </Size>
  <Size>
    <Name>003-12</Name>
    <IDs>
      <ID>0003</ID>
      <ID>0004</ID>
      <ID>0005</ID>
      <ID>0006</ID>
      <ID>0007</ID>
      <ID>0008</ID>
      <ID>0010</ID>
      <ID>0009</ID>
    </IDs>
  </Size>
</RSA>

ID应该是为了便于阅读。

修改

<Size>
  <Name>0005-24</Name>
    <IDs>
      <ID>0003</ID>
      <ID>0004</ID>
      <ID>0005</ID>
      <ID>0006</ID>
      <ID>0007</ID>
      <ID>0008</ID>
      <ID>0010</ID>   <-----
      <ID>0009</ID>   <-----
   </IDs>
</Size>

所以这些应该是有序的。

[...]
<ID>0009</ID>   <-----
<ID>0010</ID>   <-----
[...]

我尝试了很多不同的方法,但我的最后一个方法以下面的代码结束,这也不起作用。

 XElement root = XElement.Load(filePath + fileName);
 var compIDs = root.Elements()
               .GroupBy(r => r.Element("Name").Value)
               .OrderBy(xn => xn.Elements("RSA").Elements("Size").Elements("IDs").Elements("ID").ToString())
               .ToArray();
 root.ReplaceAll(compIDs);
 root.Save(filePath + fileName);

我提前搜索了SO,但找不到任何有用的东西,或者只是我不懂的东西。 ;)

问题:如何按照大小对条目进行分组,按顺序显示ID并将其替换为xml文件?

提前致谢!

3 个答案:

答案 0 :(得分:2)

您可以这样做:

var filename = ...

XDocument root = XDocument.Load(filename);

foreach (var ids_element in root.Descendants("IDs"))
{
    ids_element.ReplaceNodes(ids_element.Elements().OrderBy(x => x.Value));
}

root.Save(filename);

此代码会更改XDocument。它循环遍历name&#34; IDs&#34;的所有元素,并用这些子元素的排序版本替换子元素。

答案 1 :(得分:0)

也许这会有所帮助:

class Program
{
    static void Main(string[] args)
    {
        var root = XElement.Load("1.xml");
        var compIDs = root.Elements()
                      .GroupBy(r => r.Element("Name").Value)
                      .Select(group => new 
                                        {
                                            Group = group.Key,
                                            Items = group.First().Elements("IDs").Elements("ID")
                                                    .Select(id => new Regex("<ID>(.*)</ID>").Match(id.ToString()).Groups[1].ToString())
                                                    .OrderBy(value => value)
                                                    .ToArray()
                                        }
                              )
                      .ToArray();

        using (var sw = new StreamWriter("results.txt"))
        {
            foreach (var group in compIDs)
            {
                sw.WriteLine("Size: " + group.Group + "\r\nIDs:");
                foreach (var id in group.Items)
                    sw.WriteLine(id);
            }
        }
    }
}

产出结果:

Size: 0005-24
IDs:
0003
0004
0005
0006
0007
0008
0009
0010
Size: 0015-24
IDs:
0003
0004
0005
0006
0007
0008
0009
0010
Size: 003-12
IDs:
0003
0004
0005
0006
0007
0008
0009
0010

答案 2 :(得分:0)

编辑:这是在修改问题之前编写的,以包含XML输出。假设OP想要将数据转换为聚合的CLR对象。

我昨天在求职面试中遇到了这个问题,我也很努力,所以当我回到家时,我做了一些自己的测试,以确保它只是压力,而我的大脑并没有完全炒:)

您需要对数据进行分组,然后将其投影到新表单中,以便能够按新元素进行排序。然后,您需要告诉GroupBy您希望如何聚合它。

您可以投影到匿名类型或可以单独定义的新类。在这里,我只是使用匿名类型。

这是一个工作的控制台应用程序。我添加了一些测试数据来正确测试分组和聚合。我建议在您的生产应用程序中进行一些额外的空检查:

using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;

namespace ConsoleApplication1 {
    internal class Program {
        private static void Main(string[] args) {
            var xml = @"<RSA>
  <Size>
    <Name>0005-24</Name>
    <IDs>
      <ID>0005</ID>
      <ID>0009</ID>
    </IDs>
  </Size>
  <Size>
    <Name>0005-24</Name>
    <IDs>
      <ID>0003</ID>
      <ID>0004</ID>
      <ID>0005</ID>
      <ID>0006</ID>
      <ID>0007</ID>
      <ID>0008</ID>
      <ID>0010</ID>
      <ID>0009</ID>
    </IDs>
  </Size>
  <Size>
    <Name>0015-24</Name>
    <IDs>
      <ID>0003</ID>
      <ID>0004</ID>
      <ID>0005</ID>
      <ID>0006</ID>
      <ID>0007</ID>
      <ID>0008</ID>
      <ID>0010</ID>
      <ID>0009</ID>
      <ID>0034</ID>
      <ID>0078</ID>
    </IDs>
  </Size>
  <Size>
    <Name>003-12</Name>
    <IDs>
      <ID>0003</ID>
      <ID>0004</ID>
      <ID>0005</ID>
      <ID>0006</ID>
      <ID>0007</ID>
      <ID>0008</ID>
      <ID>0010</ID>
      <ID>0009</ID>
    </IDs>
  </Size>
</RSA>";
            var stream = new MemoryStream(Encoding.UTF8.GetBytes(xml));
            var reader = new XmlTextReader(stream);
            var root = XElement.Load(reader);

            var names = root.Elements("Size") // Get each top level size element to make the query more efficient
                .GroupBy(r => r.Element("Name").Value, // Group by the name element
                    (key, Ids) => new {
                        Name = key,
                        Ids = Ids.SelectMany(x => x.Element("IDs").Elements("ID").Select(e => e.Value)).Distinct()
                    }) // project to new form and evaluate the group
                .OrderBy(x => x.Name); // Order by the top level grouping

            foreach(var name in names) {
                Console.Write((string.Concat("Name: ", name.Name, " - ")));
                foreach(var id in name.Ids.OrderBy(x => int.Parse(x))) {
                    // Order by the integer representation (needs additional checking with TryParse())
                    Console.Write(string.Concat(id, ", "));
                }
                Console.WriteLine();
            }

            Console.ReadLine();
        }
    }
}