How to get an XNode using Linq to XML?

I

Iaml

I have an XML file and the depth of each node is unpredictable.

I need to find the parent node which contains an element whose
certain attribute has the given value. Then I will process that
parent node.

As I said, the node's depth is unpredictable, all nodes in the xml
file have the same tag name. For example, all nodes start with things
like

<Category ... ........... />
<Category ............ />

Thus if I use the regular XmlDocument, XmlNode objects, I need to
write a recursive method to find the node whose attribute has the
given value. I haven't figured out the recursive method yet. Any
thought is highly appreciated.

That said, I do notice that if I use Linq to XML, I can simply
traverse all tree nodes by

XDocument xdoc = XDocument.Load("MyXml.xml");
var categories = from category in xdoc.Descendants("Category")
select category;

foreach(var c in categories)
{
// See below for my question about this for loop.

}

In the foreach block, I can compare the attribute value with the given
value, but how do I get *only* the parent node of the node which has
this attribute value?

I tried

XElement element = c.Parent;

but, it seems that this element goes all the way to the end of the Xml
file, which is not what I want. I only want the parent node. It's
sorta easy to get the parent node only with the regular Xml API, but I
don't know how to achieve this with the Linq to XML API. Any idea?

Thank you.
 
P

prafulla

I think using xpath type expression solves the problem.Like

XDocument xdoc = XDocument.Load("MyXml.xml");
XMLNodeList nodeLiST = xdoc.SelectNodes("//category//
category[@attributeId ="certainvalue"]");


It actually searches whole xml document to find xml node(s) having
attribute of type 'attributeId' and given value.
If u know the possible path of the node then u can change xpath
expression accordingly.
Why dont u see xpath tutorial at www.w3schools.com/Xpath/ its good
place to start.

When u find the node i think u can easily find its parent.


Hope this helps.
 
T

Tim Jarvis

Iaml said:
I have an XML file and the depth of each node is unpredictable.

I need to find the parent node which contains an element whose
certain attribute has the given value. Then I will process that
parent node.

OK, just so that I think that I understand the question correctly, lets
say your xml file looks like this...

<?xml version="1.0" encoding="utf-8"?>
<Root>
<Category ID="1">
<Category ID="2">
<Category ID="3" Key="Process My Parent" />
</Category>
</Category>
<Category ID="4" />
<Category ID="5">
<Category ID="6">
<Category ID="7">
<Category ID="8" Key="Process My Parent" />
</Category>
</Category>
</Category>
</Root>

So if I am right, in this scenario you want to just return Category
with the ID="2" and Category with the ID="7" assuming that you are
looking for the attribute Key....Correct?

If this is the case then I think that the pseudo code is

Get All Category Elements At Whatever Depth
Check If It Has A child Element Called Category
if it does does it have a attribute called key - Return the element.

In Linq you can use the let clause be part of your where clause, so you
could do this....

XElement xe = XElement.Load(@"C:\Temp\cat.xml");
var list = from x in xe.DescendantsAndSelf("Category")
let pickme = x.Element("Category") != null ?
x.Element("Category").Attributes("Key").Any() : false
where pickme == true
select x;

Regards Tim.
 
I

Iaml

OK, just so that I think that I understand the question correctly, lets
say your xml file looks like this...

<?xml version="1.0" encoding="utf-8"?>
<Root>
<Category ID="1">
<Category ID="2">
<Category ID="3" Key="Process My Parent" />
</Category>
</Category>
<Category ID="4" />
<Category ID="5">
<Category ID="6">
<Category ID="7">
<Category ID="8" Key="Process My Parent" />
</Category>
</Category>
</Category>
</Root>

So if I am right, in this scenario you want to just return Category
with the ID="2" and Category with the ID="7" assuming that you are
looking for the attribute Key....Correct?

If this is the case then I think that the pseudo code is

Get All Category Elements At Whatever Depth
Check If It Has A child Element Called Category
if it does does it have a attribute called key - Return the element.

In Linq you can use the let clause be part of your where clause, so you
could do this....

XElement xe = XElement.Load(@"C:\Temp\cat.xml");
var list = from x in xe.DescendantsAndSelf("Category")
let pickme = x.Element("Category") != null ?
x.Element("Category").Attributes("Key").Any() : false
where pickme == true
select x;

Regards Tim.

Thanks Tim. But no. Suppose, I have this XML:

<?xml version="1.0" encoding="utf-8" ?>
<Root>
<Category name="mammal" Subtype="4A" KnownAs="mammal" UID="K-659H-
WT5">
<Category name="mouse" Subtype="Y4A" KnownAs="mickey"
UID="YY-83HWRR">
<Category name="goat" Subtype="88RA" KnownAs="goatee"
UID="LL6-59U-WQ" />
<Category name="cheeta" Subtype="4K5A"
KnownAs="chickeninchickenout" UID="DK6-59G621" />
<Category name="lion" Subtype="9B" KnownAs="roar" UID="LLE-66K-
KJ" />
<Category name="wolf" Subtype="2CD" KnownAs="woo"
UID="NHN-78UU">
<Category name="timber-wolf" Subtype="Q6A"
KnownAs="splasher" UID="VBD23-9Y" />
<Category name="alask-wolf" Subtype="KH9T"
KnownAs="coldblood" UID="MQ-363-UUJR" />
</Category>
</Category>
</Category>
<Category name=" ">
<!-- this type of node goes on here-->
</Category>
</Root>

Semantically, this XML does not make sense, but don't worry about it.
I just invented it for this thread. One thing that is important is
that UID is unique in this XML. So, suppose I am given UID = "MQ-363-
UUJR" (which is the last UID in the example), I would like to return
the XmlNode that *contains* the element whose UID has this given
value. In other words, I would like to an XmlNode with the following
content.

<Category name="wolf" Subtype="2CD" KnownAs="woo" UID="NHN-78UU">
<Category name="timber-wolf" Subtype="Q6A" KnownAs="splasher"
UID="VBD23-9Y" />
<Category name="alask-wolf" Subtype="KH9T" KnownAs="coldblood"
UID="MQ-363-UUJR" />
</Category>

I am able to do this with the regular XML API, though not through a
recursive method because I haven't figured out how. I am not sure how
to achieve this through Linq to XML API.

I hope that I've made the situation clear. Thank you for any hint.
 
T

Tim Jarvis

Iaml said:
Thanks Tim. But no. Suppose, I have this XML:

<?xml version="1.0" encoding="utf-8" ?>
<Root>
<Category name="mammal" Subtype="4A" KnownAs="mammal" UID="K-659H-
WT5">
<Category name="cat" Subtype="91K" KnownAs="tom" UID="E-559U" />
<Category name="dog" Subtype="47" KnownAs="snoopy" UID="659HKK88"
/
<Category name="mouse" Subtype="Y4A" KnownAs="mickey"
UID="YY-83HWRR">
<Category name="goat" Subtype="88RA" KnownAs="goatee"
UID="LL6-59U-WQ" />
<Category name="cheeta" Subtype="4K5A"
KnownAs="chickeninchickenout" UID="DK6-59G621" />
<Category name="lion" Subtype="9B" KnownAs="roar"
UID="LLE-66K- KJ" />
<Category name="wolf" Subtype="2CD" KnownAs="woo"
UID="NHN-78UU">
<Category name="timber-wolf" Subtype="Q6A"
KnownAs="splasher" UID="VBD23-9Y" />
<Category name="alask-wolf" Subtype="KH9T"
KnownAs="coldblood" UID="MQ-363-UUJR" />
</Category>
</Category>
</Category>
<Category name=" ">
<!-- this type of node goes on here-->
</Category>
</Root>

Semantically, this XML does not make sense, but don't worry about it.
I just invented it for this thread. One thing that is important is
that UID is unique in this XML. So, suppose I am given UID = "MQ-363-
UUJR" (which is the last UID in the example), I would like to return
the XmlNode that contains the element whose UID has this given
value. In other words, I would like to an XmlNode with the following
content.

<Category name="wolf" Subtype="2CD" KnownAs="woo" UID="NHN-78UU">
<Category name="timber-wolf" Subtype="Q6A" KnownAs="splasher"
UID="VBD23-9Y" />
<Category name="alask-wolf" Subtype="KH9T" KnownAs="coldblood"
UID="MQ-363-UUJR" />
</Category>

I am able to do this with the regular XML API, though not through a
recursive method because I haven't figured out how. I am not sure how
to achieve this through Linq to XML API.

I hope that I've made the situation clear. Thank you for any hint.

Ok, I think I get what you want.

In that case, I personally would still stay clear of recursion, and do
this...

Pseudo Code.

1/ Load the Doc
2/ Find the Unique Element that contains the UID ( I Assume that UID
stands for Unique ID)
3/ Find and return the Element that contains the previous found Element

i.e.

private void button1_Click(object sender, EventArgs e)
{
string uidValue = "MQ-363-UUJR";

XElement doc = XElement.Load(@"C:\Temp\cat.xml");
XElement elementWithUID = (from x in doc.DescendantsAndSelf()
where (string)x.Attribute("UID") ==
uidValue
select x).SingleOrDefault();

var list = from x in doc.DescendantsAndSelf()
where x.Elements().Contains(elementWithUID)
select x;

foreach (XElement elmt in list)
{
Debug.WriteLine(elmt.ToString());
}
}



--
 
A

Author

Ok, I think I get what you want.

In that case, I personally would still stay clear of recursion, and do
this...

Pseudo Code.

1/ Load the Doc
2/ Find the Unique Element that contains the UID ( I Assume that UID
stands for Unique ID)
3/ Find and return the Element that contains the previous found Element

i.e.

private void button1_Click(object sender, EventArgs e)
{
string uidValue = "MQ-363-UUJR";

XElement doc = XElement.Load(@"C:\Temp\cat.xml");
XElement elementWithUID = (from x in doc.DescendantsAndSelf()
where (string)x.Attribute("UID") ==
uidValue
select x).SingleOrDefault();

var list = from x in doc.DescendantsAndSelf()
where x.Elements().Contains(elementWithUID)
select x;

foreach (XElement elmt in list)
{
Debug.WriteLine(elmt.ToString());
}
}

--

OK, thank you, that helps.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top