Bullines
Bullines

Reputation: 5696

Linq Question: Combined Where Clauses

Grettings!

I have some XML that looks like this:

<Root>
    <SectionA>
        <Item id="111">
            <Options>
                <Option val="a" cat="zzz">
                    <Package value="apple" />
                    <Feature value="avacado" />
                </Option>
                <Option val="b" cat="yyy">
                    <Package value="banana" />
                    <Feature value="blueberry" />
                </Option>
            </Options>
        </Item>
        <Item id="222">
            <Options>
                <Option val="c" cat="xxx">
                    <Package value="carrot" />
                    <Feature value="cucumber" />
                </Option>
                <Option val="d" cat="www">
                    <Package value="dairy" />
                    <Feature value="durom" />
                </Option>
            </Options>
        </Item>
    </SectionA>
    <SectionB>
    .
    .
    .
    </SectionB>
</Root>

I'd like to get the PACKAGE and FEATURE values based on the ID attribute of ITEM being "111" and the VAL attribute of OPTION being "a".

I'm not sure where to start. I'm able to select the ITEM node using a where, but I'm not sure how to combine that with a where clause on the OPTION node. Any ideas?

Upvotes: 3

Views: 1089

Answers (4)

Shimmy Weitzhandler
Shimmy Weitzhandler

Reputation: 104721

And here is the VB version (VB really rocks++ in xml stuff):

Module Module1

    Sub Main()
        Dim xml As XElement = <Root>
                                  <SectionA>
                                      <Item id="111">
                                          <Options>
                                              <Option val="a" cat="zzz">
                                                  <Package value="apple"/>
                                                  <Feature value="avacado"/>
                                              </Option>
                                              <Option val="b" cat="yyy">
                                                  <Package value="banana"/>
                                                  <Feature value="blueberry"/>
                                              </Option>
                                          </Options>
                                      </Item>
                                      <Item id="222">
                                          <Options>
                                              <Option val="c" cat="xxx">
                                                  <Package value="carrot"/>
                                                  <Feature value="cucumber"/>
                                              </Option>
                                              <Option val="d" cat="www">
                                                  <Package value="dairy"/>
                                                  <Feature value="durom"/>
                                              </Option>
                                          </Options>
                                      </Item>
                                  </SectionA>
                                  <SectionB>                                      
                                  </SectionB>
                              </Root>

        Dim data = From x In xml...<Option> _
                   Where x.Ancestors("Item").@id = "111" AndAlso x.@val = "a" _
                   Select Package = x.<Package>.@value, _
                          Feature = x.<Feature>.@value

        For Each item In data
            Console.WriteLine("Package: {0}, Feature: {1}", item.Package, item.Feature)
        Next
        Stop
    End Sub

End Module

Upvotes: 0

Samuel
Samuel

Reputation: 38346

This works for me.

var doc = XDocument.Parse(s);

var items = from item in doc.Descendants("Item")
            where item.Attribute("id").Value == "111"
            from option in item.Descendants("Option")
            where option.Attribute("val").Value == "a"
            let package = option.Element("Package").Attribute("value")
            let feature = option.Element("Feature").Attribute("value")
            select new { Package = package.Value, Feature = feature.Value };

items.First().Feature; // = "avacado"
items.First().Package; // = "apple"

You can omit the let parts if you want, they are only to make the anonymous type thinner.

var items = from item in doc.Descendants("Item")
            where item.Attribute("id").Value == "111"
            from option in item.Descendants("Option")
            where option.Attribute("val").Value == "a"
            select new
            {
               Package = option.Element("Package").Attribute("value").Value,
               Feature = option.Element("Feature").Attribute("value").Value
            };

Actually, I kind of like the second one more.


And the non query Linq style.

var items = doc.Descendants("Item")
               .Where(item => item.Attribute("id").Value == "111")
               .SelectMany(item => item.Descendants("Option"))
               .Where(option => option.Attribute("val").Value == "a")
               .Select(option => new
               {
                Package = option.Element("Package").Attribute("value").Value,
                Feature = option.Element("Feature").Attribute("value").Value
               });

Upvotes: 7

Aaron Powell
Aaron Powell

Reputation: 25099

Here's a bottom-up approach:

var items = xdoc
            .Descendants("Option")
            .Where(o => (string)o.Attribute("val") == "a" && (int)o.Ancestors("Item").Single().Attribute("id") == 111)
            .Select(o => new { 
              Package = o.Element("Package"), 
              Feature= o.Element("Feature") 
            });

Upvotes: 0

Orion Edwards
Orion Edwards

Reputation: 123642

alternate implementation using SelectMany

var doc = XDocument.Parse(xml);
var items = from i in doc.Descendants("Item")
            from o in i.Descendants("Option")
            where i.Attribute("id").Value == "111"
               && o.Attribute("val").Value == "a"
         select new {
             Package = i.Descendants("Package").Attribute("value").Value,
             Feature = i.Descendants("Feature").Attribute("value").Value                     
         };

Upvotes: 4

Related Questions