I need a LINQ expression to find an XElement where the element name and attributes match an input node

StackOverflow https://stackoverflow.com/questions/155685

Question

I need to replace the contents of a node in an XElement hierarchy when the element name and all the attribute names and values match an input element. (If there is no match, the new element can be added.)

For example, if my data looks like this:

<root>
  <thing1 a1="a" a2="b">one</thing1>
  <thing2 a1="a" a2="a">two</thing2>
  <thing2 a1="a" a3="b">three</thing2>
  <thing2 a1="a">four</thing2>
  <thing2 a1="a" a2="b">five</thing2>
<root>

I want to find the last element when I call a method with this input:

<thing2 a1="a" a2="b">new value</thing2>

The method should have no hard-coded element or attribute names - it simply matches the input to the data.

Was it helpful?

Solution

This will match any given element with exact tag name and attribute name/value pairs:

public static void ReplaceOrAdd(this XElement source, XElement node)
{
    var q = from x in source.Elements()
            where x.Name == node.Name
            && x.Attributes().All(a =>node.Attributes().Any(b =>a.Name==b.Name && a.Value==b.Value))
            select x;

    var n = q.LastOrDefault();

    if (n == null) source.Add(node);
    else n.ReplaceWith(node);                                              
}

var root = XElement.Parse(data);
var newElem =XElement.Parse("<thing2 a1=\"a\" a2=\"b\">new value</thing2>");

root.ReplaceOrAdd(newElem);

OTHER TIPS

You can do an XPathSelectElement with the path (don't quote me, been to the bar; will clean up in the morn) /root/thing2[@a1='a' and @a2='b'] and then take .LastOrDefault() (XPathSelectElement is an extension method in system.Linq.Xml).

That will get you the node you wish to change. I'm not sure how you want to change it, however. The result you get is the actual XElement, so changing it will change the tree element.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top