I am trying to use Linq to XML in my c# program to extract element names from an auto-generated XSD file. This means I cannot do queries based on element names/ contents, and instead need to find elements based on their position in the xsd. I'm trying to do this with a function that returns an element's 'depth'. Somehow, if I use it in my query, I get not only the elements I need but also all underlying elements/attributes. I am guessing I need an "exclude" function in some way, but I don't know how to do this. This is the code I have at the moment:
static public void XsdReader()
{
var xsd = XDocument.Load(@"c:\XsdToLoad.xml");
var elementsAtDepthEight = from e in xsd.Descendants()
where GetElementDepth(e) == 8
select e;
foreach (var p in elementsAtDepthEight)
{
Console.WriteLine(p.ToString());
}
}
static int GetElementDepth(XElement element)
{
int result = 1;
//always return 1 as the root node has depth of 1.
//in addition, return the maximum depth returned by this method called on all the children.
if (element.Parent != null)
{
result += GetElementDepth(element.Parent);
}
return result;
}
The simplest way to measure "depth" is to count ancestors:
var elementsAtDepth8 = xsd.Descendants()
.Where(x => x.Ancestors().Count() == 8);
No need to write your own recursive code at all.
Another slightly odd alternative would be to write:
var elementsAtDepth = xsd.Elements()
.Elements()
.Elements()
.Elements()
.Elements()
.Elements()
.Elements()
.Elements();
You could obviously write a pair of recursive methods to do this:
public static IEnumerable<XElement> ElementsAtDepth(this XNode node, int depth)
{
return node.Elements().ElementsAtDepth(depth - 1);
}
public static IEnumerable<XElement> ElementsAtDepth(
this IEnumerable<XElement> elements, int depth)
{
// TODO: Validate that depth >= 0
return depth == 0 ? elements : elements.Elements().ElementsAtDepth(depth - 1);
}
See more on this question at Stackoverflow