linq в xml инициализирующий массив

У меня есть два класса - Cd и Track, которые я пытаюсь прочитать из файла XML с помощью Linq.

public Cd(string t, string a, string cat, DateTime rls, Track[] tr)
public Track(string t, int l)

XML выглядит так.

<media>
  <cd>
    <artist>Ozzy Osbourne</artist>
    <album Name="Bark at the moon" Type="Metal" Tracks="5" ReleaseDate="1983-12-10">
      <track Length="300">Bark at the moon</track>
      <track Length="235">You're No Different</track>
      <track Length="567">Now You See It (Now You Don't)</track>
      <track Length="356">Rock 'N' Roll Rebel</track>
      <track Length="120">Centre of Eternity</track>
    </album>
  </cd>
  <cd>
    <artist>Journey</artist>
    <album Name="Escape" Type="Rock" Tracks="4" ReleaseDate="1981-07-31">
      <track Length="300">Don't Stop Believin'</track>
      <track Length="235">Stone in Love</track>
      <track Length="567">Who's Crying Now</track>
      <track Length="356">Keep on Runnin'</track>
    </album>
  </cd>
</media>

Код, который я пытаюсь использовать, выглядит следующим образом

XElement xdoc = XElement.Load("dbxml.xml");
    var temp = from cds in xdoc.Descendants("cd")
    select new Cd(
        cds.Element("artist").Value,
        cds.Element("album").Attribute("Name").Value,
        cds.Element("album").Attribute("Type").Value,
        DateTime.Parse(cds.Element("album").Attribute("ReleaseDate").Value),
        new Track[] { // One Track reads fine..
            new Track(cds.Element("album").Element("track").Value,
               int.Parse(cds.Element("album").Element("track").Attribute("Length").Value)) 
               }
        );

Проблема в том, что я не знаю, как инициализировать массив со всеми дорожками, прочитанными из файла XML. Я мог бы обернуть весь запрос в .ToList(), использовать анонимный тип и foreach-it, но я хотел бы знать, есть ли способ сделать это всего за один запуск с linq.

cds.Elements("album").Elements("song") возвращает коллекцию IEnumerable<XElement>, и ее нужно каким-то образом добавить в массив как диапазон и преобразовать в строку и int или что-то подобное. Есть какая-нибудь помощь?

Спасибо!


person citizencane    schedule 09.02.2011    source источник


Ответы (2)


Это сработает:

XElement xdoc = XElement.Load("test.xml");
var temp = from cds in xdoc.Descendants("cd")
            select new Cd(
                cds.Element("artist").Value,
                cds.Element("album").Attribute("Name").Value,
                cds.Element("album").Attribute("Type").Value,
                DateTime.Parse(cds.Element("album").Attribute("ReleaseDate").Value),
                cds.Element("album").Descendants("track").Select(t => new Track(t.Value, int.Parse(t.Attribute("Length").Value))).ToArray() 
                );

Легче читать (imo), используя свойства:

select new Cd()
    {
        Artist = cds.Element("artist").Value,
        Album = cds.Element("album").Attribute("Name").Value,
        Type = cds.Element("album").Attribute("Type").Value,
        ReleaseDate = DateTime.Parse(cds.Element("album").Attribute("ReleaseDate").Value),
        Tracks = cds.Element("album")
                    .Descendants("track")
                    .Select(t => new Track()
                    {
                      Name = t.Value,
                      Length = int.Parse(t.Attribute("Length").Value)
                    }).ToArray()
    };

Вам следует подумать о добавлении конструктора по умолчанию в Cd и Track и раскрытии общедоступных свойств или использовании именованных параметров - это сделало бы оператор LINQ более читабельным.

person BrokenGlass    schedule 09.02.2011
comment
Это сработало из коробки - спасибо! На самом деле у меня есть конструктор и свойства по умолчанию для всех полей. Не могли бы вы подробнее рассказать о том, как этот запрос будет более читаемым при использовании свойств? - person citizencane; 09.02.2011
comment
Я считаю, что присвоения свойств легче анализировать как читатель, так что это может быть, например, select new Cd() { Artist = ... , Album = ... , }, то же самое для треков - person BrokenGlass; 10.02.2011
comment
Да, это кажется более читаемым запросом. Спасибо! Мне нужно еще немного изучить мой C #. :-) - person citizencane; 10.02.2011

В вашем LINQ замените

new Track[] { // One Track reads fine..
    new Track(cds.Element("album").Element("track").Value,
       int.Parse(cds.Element("album").Element("track").Attribute("Length").Value)) 
       }

примерно так:

( from t in cds.Element("album").Elements("track") 
  select new Track(..., ...)
).ToArray()
person Donald Byrd    schedule 09.02.2011
comment
Спасибо! Хотя я получил первый трек каждого альбома, повторенный для всех остальных треков. Пять Лай на Луну и четыре Не переставай верить. Может я что то напортачил? - person citizencane; 09.02.2011
comment
Я опечатал Element и IIRC, это элементы, которые вам нужны. Ответ BrokenGlass использует .Descendants, что может быть лучшим способом. - person Donald Byrd; 10.02.2011