collecting data from IEnumerable.Descendants


  • I've got code that looks like

                    IEnumerable<XElement> controlnode = (from controlnodes in report.Descendants(Globals.xnControl)
                                                         where (controlnodes.Attribute(Globals.xnControl).Value == control)
                                                         select controlnodes);
                    IEnumerable<XElement> sources = (from finding in controlnode.Descendants(Globals.xnFinding)
                                                     where (finding.Attribute(Globals.xnToolIDNode).Value == toolid)
                                                     select finding.Element(Globals.xnSourcesNode));
                    IEnumerable<XElement> lrus = (from sourcesnode in sources 
                                                  where sourcesnode.Attribute(Globals.xnSite).Value == site.ToString()
                                                  select sources.Descendants(Globals.xnFinding)); 

    but the last IEnumerable<XElement> lrus is throwing a casting error.  I've got two questions.

    How do I fix this casting error?

    I'm assuming that these multiple sequential linq lines would be more optimal written as one linq line.  How would I do that?

    Thanks, you've all been great with the support.

    Thursday, June 21, 2012 4:14 PM

All replies

  • Please, next time when you get an error message then post the exact error message you get. And of course you should show us a sample of the XML input data you want to query and explain what kind of result you are looking for.

    As far as I can tell based on reading your lines of code, your variable "lrus" is typed as "IEnumerable<XElement>" while the last query returns an "IEnumerable<IEnumerable<XElement>>" as you have

       select sources.Descendants(...)

    As for fixing that, we don't know what you want, if the type you have declared is correct then you might only want a single descendant with e.g.

      select sources.Descendants(Global.xnFinding).FirstOrDefault()

    Or declare the variable as

      IEnumerable<IEnumerable<XElement>> lrus = ...

    then your original query should do I think.

    As for combining your queries, I will show you that for the first two, then you can hopefully do it for all three, if not then clarify above details and we help further.

      IEnumerable<XElement> sources = from controlnode in report.Descendants(Globals.xnControl)

                                                     where (string)controlnode.Attribute(Globals.xnControl) == control

                                                     from finding in controlnode.Descendants(Globals.xnFinding)

                                                     where (string)finding.Attribute(Globals.xnToolIDNode) == toolid

                                                     select finding.Element(Globals.xnSourcesNode);

    MVP Data Platform Development My blog

    • Proposed as answer by Martin Honnen Thursday, June 21, 2012 5:37 PM
    • Unproposed as answer by Newmanb1 Thursday, June 21, 2012 6:07 PM
    Thursday, June 21, 2012 4:46 PM
  • Thanks, what I want is an IEnumerable<XElement> of all Finding nodes.

    Here's a schema of the source data

    <?xml version="1.0" encoding="UTF-8"?>
    <!--W3C Schema generated by XMLSpy v2012 rel. 2 sp1 (>
    <xs:schema xmlns:xs="">
    	<xs:complexType name="T_Sources">
    			<xs:element ref="LRUs"/>
    		<xs:attribute ref="UResponse" use="required"/>
    		<xs:attribute ref="UComment" use="required"/>
    		<xs:attribute ref="Site" use="required"/>
    		<xs:attribute ref="Example" use="required"/>
    		<xs:attribute ref="AComment" use="required"/>
    	<xs:complexType name="T_Root">
    			<xs:element ref="Control" maxOccurs="unbounded"/>
    	<xs:complexType name="T_LRUs">
    			<xs:element ref="LRU" minOccurs="0" maxOccurs="unbounded"/>
    	<xs:complexType name="T_Findings">
    			<xs:element ref="Finding" maxOccurs="unbounded"/>
    	<xs:complexType name="T_Finding">
    			<xs:element ref="Sources"/>
    		<xs:attribute ref="ToolID" use="required"/>
    		<xs:attribute ref="Mitigation" use="required"/>
    		<xs:attribute ref="Description" use="required"/>
    	<xs:complexType name="T_Control">
    			<xs:element ref="Findings"/>
    		<xs:attribute ref="Severity" use="required"/>
    		<xs:attribute ref="Impact" use="required"/>
    		<xs:attribute ref="IA_Control" use="required"/>
    	<xs:attribute name="UResponse" type="xs:string"/>
    	<xs:attribute name="UComment" type="xs:string"/>
    	<xs:attribute name="ToolID" type="xs:string"/>
    	<xs:attribute name="Site" type="xs:string"/>
    	<xs:attribute name="Severity" type="xs:string"/>
    	<xs:attribute name="Mitigation" type="xs:string"/>
    	<xs:attribute name="Impact" type="xs:string"/>
    	<xs:attribute name="IA_Control" type="xs:string"/>
    	<xs:attribute name="Example" type="xs:string"/>
    	<xs:attribute name="Description" type="xs:string"/>
    	<xs:attribute name="AComment" type="xs:string"/>
    	<xs:element name="Sources" type="T_Sources"/>
    	<xs:element name="Root" type="T_Root"/>
    	<xs:element name="LRUs" type="T_LRUs"/>
    	<xs:element name="LRU" type="xs:string"/>
    	<xs:element name="Findings" type="T_Findings"/>
    	<xs:element name="Finding" type="T_Finding"/>
    	<xs:element name="Control" type="T_Control"/>
    I didn't think the question had anything to do with the schema, but if it helps, here it is.  I can't provide a populated XML file as the data is proprietary.

    • Edited by Newmanb1 Thursday, June 21, 2012 6:26 PM
    Thursday, June 21, 2012 6:21 PM
  • Sorry but if all you "want is an IEnumerable<XElement> of all Finding nodes" then all you need is

      IEnumerable<XElement> findings = report.Descendants("Finding");

    or if you prefer the query syntax then

      IEnumerable<XElement> findings = from finding in report.Descendants("Finding") select finding;

    As the code sample you have posted uses variables it is hard to tell which element or attributes defined in the schema you are looking for. If the variable names relate to schema element names then doing

    IEnumerable<XElement> sources = from controlnode in report.Descendants(Globals.xnControl)

                                                     where (string)controlnode.Attribute(Globals.xnIA_Control) == control

                                                     from finding in controlnode.Descendants(Globals.xnFinding)

                                                     where (string)finding.Attribute(Globals.xnToolIDNode) == toolid

                                                     select finding;

    might suffice to give an IEnumerable<XElement> of "Finding" element nodes. The further queries you have written suggest you want to check or even select other nodes but I am not able to tell which ones to make suggestions on the code. If you can't solve it yourself with the help of then we will need to see more details, if the data in the sample is proprietary then consider to post some mockup of sample data.

    MVP Data Platform Development My blog

    Friday, June 22, 2012 10:14 AM
  • Hi,

    Could you explain which elements you want to retrieve in lrus?

    Kind regards,

    My blog

    Whether you’re a construction worker, a forum moderator, or just someone that likes helping people. I think these guidelines can be helpful in keeping you helpful when being helpful.

    Friday, June 22, 2012 11:19 PM