2016-01-14 5 views
0

Я немного застрял и не могу думать прямо, может быть, вы мне поможете.Linq to XML GroupBy then OrderBy

Мой .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> 

И идентификаторы должны быть для того, чтобы читаемости.

Edit:

<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 заранее, но не нашел ничего полезного, или это только я, который не понимает. ;)

Вопрос: Как сгруппировать записи по их размеру, привести идентификаторы в порядок и заменить их в XML-файле?

Заранее благодарен!

+3

Почему вы хотите группировать по размеру? они уже сгруппированы по размеру в XML. Каков ожидаемый результат? –

+0

Можете ли вы уточнить, что вы не работаете? Выдает ли ошибка?Если нет, то какой результат? – Rob

+0

И каков ожидаемый результат? например анонимного типа или какого-то нового «XElement». –

ответ

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 на месте. Он перебирает все элементы имен «ID» и заменяет дочерние элементы на сортированную версию таких дочерних элементов.

+0

Очевидно, я бежал в неправильном направлении и смущал вещи! Большое спасибо! – Moemoe

+0

Yacoub, хороший ответ! +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 
0

EDIT: Это было написано до того, как был изменен вопрос с включением вывода XML. Предполагалось, что OP хочет преобразовать данные в агрегированный объект CLR.

У меня была эта точная проблема в качестве теста на собеседовании вчера, в котором я также боролся, поэтому я сделал несколько собственных тестов, когда вернулся домой, чтобы убедиться, что это просто давление, t полностью жареный :)

Вам нужно сгруппировать свои данные, а затем проецировать в новую форму, чтобы иметь возможность заказывать новые элементы. Затем вам нужно сообщить 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(); 
     } 
    } 
}