Palle Due
Palle Due

Reputation: 6292

Selecting from xml based on attributes in multiple subnodes

I have the following xml file:

<?xml version="1.0" encoding="utf-8"?>
<nodes>
  <node id="1">
    <subnode name="a" value="1" />
    <subnode name="b" value="2" />
  </node>
  <node id="2">
    <subnode name="a" value="2" />
    <subnode name="b" value="2" />
  </node>
  <node id="3">
    <subnode name="a" value="1" />
    <subnode name="b" value="1" />
  </node>
  <node id="4">
    <subnode name="a" value="1" />
    <subnode name="b" value="2" />
  </node>
</nodes>

I need to select the id's of the nodes that have both a=1 and b=2 (in this case node 1 and node 4).

I would prefer to do it using linq, and I have the following code to select those that have a=1. How do I expand the code to also take care of the second requirement?

var document = XDocument.Load(@"c:\temp\subnodes.xml");

var x = from topnode in document.Descendants("nodes")
   let nodes = topnode.Descendants("node") from n in nodes
   let subnodes = n.Descendants("subnode") from s in subnodes
   where s.Attribute("name").Value == "a" && s.Attribute("value").Value == "1"
   select n.Attribute("id").Value;

EDIT: I've made a .NET Fiddle here: https://dotnetfiddle.net/mOg3wv

Upvotes: 0

Views: 375

Answers (2)

Aarif
Aarif

Reputation: 1675

UPDATE 2:
here's the LINQ based solution for this

 var nodes = document.Descendants("nodes").Descendants("node");

 return (from node in nodes
        let aSubNode = node.Descendants("subnode")
          .FirstOrDefault(a => a.Attribute("name")?.Value == "a" && a.Attribute("value")?.Value == "1")
        let bSubNode = node.Descendants("subnode")
          .FirstOrDefault(a => a.Attribute("name")?.Value == "b" && a.Attribute("value")?.Value == "2")
        where aSubNode != null && bSubNode != null
        select node.Attribute("id")?.Value).ToList();

UPDATE 1:
I updated the code to read id attribute value from nodeelement instead of subnode.


ORIGINAL:
following isn't all LINQ but this should solve your problem

var nodes = document.Descendants("nodes").Descendants("node");

var ids=new List<string>();

foreach (var node in nodes)
{
       var aSubNode = node.Descendants("subnode")
            .FirstOrDefault(a => a.Attribute("name")?.Value == "a" && a.Attribute("value")?.Value == "1");
       var bSubNode=node.Descendants("subnode")
           .FirstOrDefault(a => a.Attribute("name")?.Value == "b" && a.Attribute("value")?.Value == "2");

       if (aSubNode==null || bSubNode==null)
           continue;
       ids.Add(node.Attribute("id")?.Value);
}

Upvotes: 1

Anu Viswan
Anu Viswan

Reputation: 18155

You can use following.

var result = document.Descendants("node").Where(x=>x.Descendants("subnode")
            .All(c=>(c.Attribute("name").Value == "a" && c.Attribute("value").Value=="1") || 
                    (c.Attribute("name").Value == "b" && c.Attribute("value").Value=="2")))
            .Select(x=>x.Attribute("id").Value);

Output

1 
4 

Upvotes: 1

Related Questions