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!