1
\$\begingroup\$

I want to traverse a xml tree until I find the first element whose position matches the path. So if I'm looking for Foo/Bar/FooBar I want to get this:

<Root>
  <Foo>
    <Bar>
      <FooBar /> <!-- This one -->
    </Bar>
  </Foo>
</Root> 

However there could be any number of <FooBar />'s in my way and I only want the one at this path. I wrote 2 methods to get this:

private static XElement ElementByName(XElement parent, string name)
{
  return parent.Elements().Where(element => element.Name.LocalName == name).First();
}

private static XElement ElementAtPath(XElement root, string path)
{
  XElement target = root;
  string[] pathArr = path.Split('/');
  for (int i = 0; i < pathArr.Length; i ++)
    {
      target = ElementByName(target, pathArr[i]);
    }
  return target;
}

Is there a faster / more efficient way I could achieve this?

\$\endgroup\$
  • 1
    \$\begingroup\$ There is, look here - you need to use XPath ;-) \$\endgroup\$ – t3chb0t Sep 27 '18 at 14:59
  • \$\begingroup\$ Using (XElement)root.XPathSelectElement(path); doesn't work for me \$\endgroup\$ – Peter Sep 27 '18 at 15:36
  • \$\begingroup\$ Maybe XMLreader and then you stop at the first. \$\endgroup\$ – paparazzo Sep 27 '18 at 16:24
  • 2
    \$\begingroup\$ I removed the performance tag because you hadn't provided any benchmarks and sample data and without them no one can actually tell you which approach is or would be faster. \$\endgroup\$ – t3chb0t Sep 28 '18 at 5:41
2
\$\begingroup\$

You could try the following which utilizes XPath's XPathSelectElement extension method.

test.xml

<Root>
  <Foo>
      <FooBar>1</FooBar>
    <Bar>
      <FooBar>2</FooBar>
    </Bar>
  </Foo>
</Root>

C#

using System;
using System.Xml.Linq;
using System.Xml.XPath;

...

    static void Main(string[] args)
    {
        XDocument document = XDocument.Load(@"test.xml");
        XElement element = ElementAtPath(document.Root, "Foo/Bar/FooBar");

        Console.WriteLine(element); // Prints <FooBar>2</FooBar>
    }

    private static XElement ElementAtPath(XElement root, string path)
    {
        if (root == null)
        {
            throw new ArgumentNullException(nameof(root));
        }

        if (string.IsNullOrWhiteSpace(path))
        {
            throw new ArgumentException("Invalid path.");
        }

        return root.XPathSelectElement(path);
    }
\$\endgroup\$
0
\$\begingroup\$

What about:

private static XElement ElementByName(XElement parent, string name)
{
  // Use FirstOrDefault() or another selector to find the relevant element
  return parent.Descendants(name).FirstOrDefault();
}

Usage:

XDocument document = XDocument.Load(@"<path to file>");
var result = ElementByName(document.Root, "FooBar");
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.