TRS
TRS

Reputation: 2097

LINQ to XML query returning wrong data

I have this xml

<?xml version="1.0" encoding="utf-8" ?>
<Departments>
 <Department>
  <id>001</id>
  <Section>
    <SectionId>001001</SectionId>
    <Room>
      <RoomID>001001001</RoomID>
      <Owner>guest1</Owner>
    </Room>
    <Room>
      <RoomID>001001002</RoomID>
      <Owner>guest11</Owner>
    </Room>
  </Section>
  <Section>
    <SectionId>001002</SectionId>
    <Room>
      <RoomID>001002001</RoomID>
      <Owner>guest2</Owner>
    </Room>
 </Section>
</Department>
</Departments>  

and this is my code using Linq to Xml

var xDoc = XDocument.Load(inputUrl);

var sections = from el in xDoc.Descendants("Department")
                       where el.Element("id").Value.Equals("001")
                       select el.Element("Section");

var rooms = from el in sections
               where el.Element("SectionId").Value.Equals("001001")
               select el.Element("Room");

var roomsList = (from el in rooms
            select new Room
            {
                roomID = (string)el.Element("RoomID"),
                owner = (string)el.Element("Owner")
            }).ToList();

My problem is I only get 1 room in the List,but I should get two.Please also advice if this is the right way of using LINQ to xml,I am fairly new to LINQ.

Upvotes: 1

Views: 858

Answers (5)

Alex Filipovici
Alex Filipovici

Reputation: 32571

As an alternative to the other answers, you could use the Extensions.XPathSelectElements Method (XNode, String) (make sure you add the using System.Xml.XPath directive to the top of your file):

string 
    departmentId = "001", 
    sectionId = "001001";
var xDoc = XDocument.Load(inputUrl);
var rooms = xDoc.XPathSelectElements(
    String.Format(
        "//Department[id={0}]/Section[SectionId={1}]/Room",
        departmentId,
        sectionId))
    .Select(el => new Room
    {
        roomID = (string)el.Element("RoomID"),
        owner = (string)el.Element("Owner")
    }).ToList();

It's a matter of own preference. I find this shorter to write and easier to read.

Upvotes: 2

Matthew Watson
Matthew Watson

Reputation: 109792

Just to show that there are many ways to skin a cat:

var xDoc = XDocument.Load(@"C:\TEST\TEST.XML");

var depts = from e in xDoc.Descendants("Department")
            where e.Element("id").Value.Equals("001")
            select e;

var sections = from e in depts.Descendants("Section")
            where e.Element("SectionId").Value.Equals("001001")
            select e;

var rooms = (from e in sections.Descendants("Room")
            select new //Room 
            {
                ID = (string)e.Element("RoomID"),
                Owner = (string)e.Element("Owner")
            }).ToList();

Upvotes: 1

Matten
Matten

Reputation: 17603

You're only selecting one room in the middle query, it should be .Elements(...) (pay attention to the trailing s):

        var rooms = from el in sections
                       where el.Element("SectionId").Value.Equals("001001")
                       select el.Elements("Room");

The same applies to your section query.

Upvotes: 0

MarcinJuraszek
MarcinJuraszek

Reputation: 125650

Change sections and rooms queries to:

var sections = xDoc.Descendants("Department")
                   .FirstOrDefault(x => (string)x.Element("id") == "001")
                   .Elements("Section");

var rooms = sections.Where(x => (string)x.Element("SectionId") == "001001")
                    .Elements("Room");

With these, you'll get 2 rooms.

Why isn't your code working?

  1. select el.Element("Section") selects only first section element within Department - you'd never be able to get room from section with id == "001002"
  2. select el.Element("Room") in rooms query returns only first room from every matched section.

You can change Element to Elements and add additional SelectMany(x => x) calls to make your syntax-based queries work:

var sections = from el in xDoc.Descendants("Department")
                where el.Element("id").Value.Equals("001")
                select el.Elements("Section");

var rooms = from el in sections.SelectMany(x => x)
            where el.Element("SectionId").Value.Equals("001001")
            select el.Elements("Room");

var roomsList = (from el in rooms.SelectMany(x => x)
                    select new 
                    {
                        roomID = (string)el.Element("RoomID"),
                        owner = (string)el.Element("Owner")
                    }).ToList();

Upvotes: 2

Giannis Paraskevopoulos
Giannis Paraskevopoulos

Reputation: 18411

What if you change:

select el.Element("Room");

to

select el.Elements("Room");

Upvotes: 0

Related Questions