tags:

views:

838

answers:

5

Greetings!

I'm working on wrapping my head around LINQ. If I had some XML such as this loaded into an XDocument object:

<Root>
    <GroupA>
        <Item attrib1="aaa" attrib2="000" attrib3="true" />
    </GroupA>
    <GroupB>
        <Item attrib1="bbb" attrib2="111" attrib3="true" />
        <Item attrib1="ccc" attrib2="222" attrib3="false" />
        <Item attrib1="ddd" attrib2="333" attrib3="true" />
    </GroupB>
    <GroupC>
        <Item attrib1="eee" attrib2="444" attrib3="true" />
        <Item attrib1="fff" attrib2="555" attrib3="true" />
    </GroupC>
</Root>

I'd like to get the attribute values of all of the Item child elements of a Group element. Here's what my query looks like:

var results = from thegroup in l_theDoc.Elements("Root").Elements(groupName)
              select new
              { 
                 attrib1_val = thegroup.Element("Item").Attribute("attrib1").Value,      
                 attrib2_val = thegroup.Element("Item").Attribute("attrib2").Value,
              };

The query works, but if for example the groupName variable contains "GroupB", only one result (the first Item element) is returned instead of three. Am I missing something?

+2  A: 

Yes, .Element() only returns the first matching element. You want .Elements() and you need to re-write your query somewhat:

var results = from group in l_theDoc.Root.Elements(groupName)
              select new
              {
                  items = from i in group.Elements("Item")
                          select new 
                          {
                              attrib1_val = i.Attribute("attrib1").Value,
                              attrib2_val = i.Attribute("attrib2").Value
                          }
              };
C. Lawrence Wenham
General idea is right, but it has at least 2 syntax errors and 2 unnecessary operations :)
aku
Yes you cant use group as a variable name, since its a linq keyword.
Jim Burger
1) group - reserved kw. 2) l_theDoc.Root - no such thing 3) no need to wrap items in separate objects 4) .Value can be safely omitted
aku
+5  A: 
XElement e = XElement.Parse(testStr);

string groupName = "GroupB";
var items = from g in e.Elements(groupName)
            from i in g.Elements("Item")
            select new {
                           attr1 = (string)i.Attribute("attrib1"),
                           attr2 = (string)i.Attribute("attrib2")
                       };

foreach (var item in items)
{
    Console.WriteLine(item.attr1 + ":" + item.attr2);
}
aku
A: 

Another possibility is using a where clause:

var groupName = "GroupB";
var results = from theitem in doc.Descendants("Item")
              where theitem.Parent.Name == groupName
              select new 
              { 
                  attrib1_val = theitem.Attribute("attrib1").Value,
                  attrib2_val = theitem.Attribute("attrib2").Value, 
              };
Jim Burger
pretty perverse logic IMO. Usually XML tree being traversed from parent to child nodes
aku
All depends on whether you have more children than parents ;) - just making sure all the possibilities are shown.
Jim Burger
Yep, it's somewhat original solution :)
aku
+1  A: 

Here's the query method form of the answer:

var items = 
  e.Elements("GroupB")
    .SelectMany(g => g.Elements("Item"))
    .Select(i => new {
      attr1 = i.Attribute("attrib1").Value,
      attr2 = i.Attribute("attrib2").Value,
      attr3 = i.Attribute("attrib3").Value
    } )
    .ToList()
David B
Good to see this form!
Jim Burger
unlike other methods this one is doomed to throw NullReferenceException :)
aku
Considering that the compiler translates your answer to this, where's the NullReferenceException?
David B
David, my apologize. I mistaken behavior of Select with First (last can throw exception) Only place where your code can fail - Value property. If attribute is missing Value property will cause exception. Also compiler translates code to slightly different (but equivalent) representation
aku
A: 

how would you ammend that so you could return all the results from all groups where say the attrib3 value is true