views:

679

answers:

2

Using LINQ to XML, XElements and XAttributes I'd like to filter a query using an exact match on an XElement's Attributes() IEnumerable collection of XName/Value pairs, against a provided IEnumerable Attributes collection of XName/Value pairs.

<container>
   <!-- logical group of settings -->
   <group name="group-one">
      <!-- setting with multiple key-value key options to return value -->
      <setting>
         <key app="a" command="go" />
         <key app="a" type="z" command="go" />
         <key app="a" type="z" flag="1" command="go" />
         <key app="a" type="z" target="OU812"/>
         <value>group-one item value A</value>
      </setting>
      <setting>
         <key app="a" />
         <key app="a" type="z" />
         <key app="a" type="z" flag="1" />
         <key app="c" type="z" command="go" />
         <value>group-one item value B</value>
      </setting>      
      ...
      ...
   </group>
   <group name="group-two">
      ...
      ...
   </group>
</container>

I suspect using Attributes().SequenceEqual() with a custom IEqualityComparer to compare both XAttribute.XName and XAttribute.Value is the way to go, but the sequence never matches because XElement("key").Attributes() returns a collection of all attributes for all elements in a given element. I have tried any number linq queries similar to following code snippet, including nested queries and using the "let" statement, but I can't seem to get it right.

var myAttributes = new List<XAttribute> {
                                        new XAttribute("app", "a"), 
                                        new XAttribute("type", "z")
                                        };

var xGroup = doc.Elements("group").First(g=>g.Attribute("name").Value==groupName);
var xSetting = (from s in xGroup.Elements("setting")
               // AttributesComparer() compares both the XAttribute.XName and XAttribute.Value properties to determine equality
               where s.Elements("key").Attributes().SequenceEqual(myAttributes, new AttributesComparer())
               select s).FirstOrDefault();

var xValue = xSetting.Element("value");

It should return an XElement representing <value>group-one item value B</value>

For now, I have settled on using XPathSelectElement() -- passing an XPath string built from the name/values in the provided XAttributes() collection. In this case, the generated string would be:

group[@name='group-one']/setting[key[@app='n' and @type='z' and count(@*)=2]]/value

But, I'm generally resistant to looping through collections to build strings in an application. I have been hammering away at this for a while now, and unfortunately, I am stumped as to how to impliment a more "elegant" solution using a compact linq query (or series of queries) with existing methods.

Any feedback and/or suggestions would be greatly appreciated!

+1  A: 

You need to find a setting where any key matches so use the Any extension method. Only a small change is required:

    var xSetting = (from s in xGroup.Elements("setting")
                    // AttributesComparer() compares both the XAttribute.XName and XAttribute.Value properties to determine equality
                    where s.Elements("key").Any(key => key.Attributes().SequenceEqual(myAttributes, new AttributesComparer()))
                    select s).FirstOrDefault();
Mark Byers
Very nice, Mr. Byers. Thank you very much. Sometimes we all just need that little tidbit from another developer to put us over the finish line!
Mad Man Moon
A: 

Could you post the code for your AttributesComparer class?

Loathian