2

How I can get a list of all genres? Now I only get the first genre of each song.

XML file:

<Library xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Songs>
    <Song>
      <Title>High Hopes</Title>
      <Genres>
        <Genre>Rock</Genre>
        <Genre>American</Genre>      
      </Genres>
     </Song>
    <Song>
      <Title>Imagine</Title>
      <Genres>
        <Genre>Pop</Genre>
        <Genre>Unplugged</Genre>      
      </Genres>
    </Song>
  </Songs>
</Library>

C# code:

public void ListGenres()
{
  System.Xml.Linq.XElement xLibrary = System.Xml.Linq.XElement.Load(@"c:\Library.xml");
  System.Xml.Linq.XElement xSongs = xLibrary.Element("Songs");
  System.Collections.Generic.IEnumerable<string> genres = 
                from code in xSongs.Elements("Song")
                let genre = (string)code.Element("Genres").Element("Genre")
                orderby genre
                select genre;
  foreach (string genre in genres)
  {
      Console.WriteLine(genre);
  }
}

Result:

Pop
Rock

But I need:

Rock
American
Pop
Unplugged

Thanks.

Andrey Korneyev
  • 26,353
  • 15
  • 70
  • 71
jaile
  • 21
  • 1
  • 3

7 Answers7

3

Since you are not performing any filters, you can directly use Descendants like this:-

XDocument xLibrary = XDocument.Load(@"c:\Library.xml");
IEnumerable<string> result = xLibrary.Descendants("Genre").Select(x => (string)x);

Or if you prefer query syntax:-

IEnumerable<string> res = from genre in xdoc.Descendants("Genre")
                                      select (string)genre;

You need to import using System.Linq; for this.This will produce the outpu you expect i.e. Rock American Pop Unplugged even though it is not ordered. You can always use order by caluse for that.

Check this answer to understand why I am using Descendants instead of Elements.

Community
  • 1
  • 1
Rahul Singh
  • 21,585
  • 6
  • 41
  • 56
1

If you don't need any additional filtering of genres, you can easily get all the genres in document like this:

var genres = xLibrary.XPathSelectElements("//Genre").Select(g => g.Value);

Note: XPathSelectElements is an extension method, make sure you're using System.Xml.XPath namespace in your sourcecode in order to use this method.

Andrey Korneyev
  • 26,353
  • 15
  • 70
  • 71
1

In case your xml also contains "Genre" nodes for elements other than songs:

XDocument doc = XDocument.Parse(xml);

var genres = doc.Root.Element("Songs")
                     .Elements("Song")
                     .Elements("Genres")
                     .Elements("Genre")
                     .Select(e => e.Value);
w.b
  • 11,026
  • 5
  • 30
  • 49
0

The problem is you are selecting a single genre using .Element(string), you should use .GetElements(string) if you dont wanna use .Descendants(string):

IEnumerable<string> genres = xSongs
    .Elements("Song")
    .SelectMany(song => song
        .Element("Genres")
        .Elements("Genre")
        .Select(genre => genre.Value));

The linq above is equivalent to "xSongs.(many)Song.Genres.(many)Genre.Value"

And if you rather use query syntax:

IEnumerable<string> genres = from song in xSongs.Elements("Song")
                             let songGenres = song.Element("Genres").Elements("Genre")
                             from genre in songGenres
                             select genre.Value;

Since you have more than one one-to-many relation in your xml, you need to have more than one from in your linq query.

Tamir Vered
  • 10,187
  • 5
  • 45
  • 57
0

Assuming you need ordered list of genre for each song. You can try below code.

public void ListGenres()
{
  System.Xml.Linq.XElement xLibrary = System.Xml.Linq.XElement.Load(@"c:\Library.xml");
  System.Xml.Linq.XElement xSongs = xLibrary.Element("Songs");
  System.Collections.Generic.IEnumerable<List<string>> genresList = 
                from code in xSongs.Elements("Song")
                let genreList = (string)code.Element("Genres")
                select genreList.ToList();
  foreach (List<string> genreList in genresList)
  {
     var orderedList = genreList.OrderBy(x=>x);
     foreach(string genre in orderedList)
      Console.WriteLine(genre);
  }
}
Viru
  • 2,228
  • 2
  • 17
  • 28
0

Try this

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;

namespace ConsoleApplication52
{
    class Program
    {
        static void Main(string[] args)
        {
            string input =
                "<Library xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">" +
                  "<Songs>" +
                    "<Song>" +
                      "<Title>High Hopes</Title>" +
                      "<Genres>" +
                        "<Genre>Rock</Genre>" +
                        "<Genre>American</Genre>" +
                      "</Genres>" +
                     "</Song>" +
                    "<Song>" +
                      "<Title>Imagine</Title>" +
                      "<Genres>" +
                        "<Genre>Pop</Genre>" +
                        "<Genre>Unplugged</Genre>" +
                      "</Genres>" +
                    "</Song>" +
                  "</Songs>" +
                "</Library>";

            XElement library = XElement.Parse(input);

            var results = library.Descendants("Song").Select(x => new
            {
                title = x.Element("Title").Value,
                genres = x.Descendants("Genre").Select(y => new {
                    genere = y.Value
                }).ToList()
            }).ToList();
        }
    }
}
jdweng
  • 33,250
  • 2
  • 15
  • 20
0
IEnumerable<string> genres = xSongs.Elements("Song")
    .Select(song => song.Element("Genres"))
    .SelectMany(genres => genres.Elements("Genre"))
    .Select(genre => genre.Value)
    .OrderBy(x => x);
Jan Palas
  • 1,865
  • 1
  • 23
  • 35