0

I have two methods:

  • the first one parses a xml string into some XElements (where the xml is a sequence of 'banana' elements)
  • the second one extracts the value from a child 'id' element and returns it

I have two alternative implementations of 'GetBananaId(XElement element)' - can anyone explain why the second 'wrong' implementation doesn't give the child element relative to its XElement parameter?

public void TestHarness()
{
    var xml = "<bananas><banana><id>A</id></banana><banana><id>B</id></banana><banana><id>C</id></banana></bananas>";
    foreach (var element in GetBananaElements(xml))
    {
        var right = GetBananaId(element);
        var wrong = GetBananaId_WRONG(element);
        Console.WriteLine("Right: {0}, Wrong: {1}", right, wrong);
    }
}

public IEnumerable<XElement> GetBananaElements(string recordsXml)
{
    var recordsXDoc = XDocument.Parse(recordsXml);
    return recordsXDoc.XPathSelectElements("//banana");
}

public string GetBananaId(XElement element)
{
    return element.Element("id").Value;
}

public string GetBananaId_WRONG(XElement element)
{
    return element.XPathSelectElement("//id").Value;
}

These produce the console output:

Right: A, Wrong: A
Right: B, Wrong: A
Right: C, Wrong: A
rogersillito
  • 869
  • 1
  • 10
  • 27

1 Answers1

1

Use a relative path return element.XPathSelectElement("id").Value;, with //id you search down from / which is the document node.

Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
  • Thanks Martin - that fixes the issue. To fully answer my question I would really like to know why this happens though: I'm calling XPathSelectElement("//id") on the XElement, yet this behaviour suggests this method actually sees the DOM of the complete document? – rogersillito Jan 21 '14 at 09:35
  • 1
    Yes, sure, XPath allows you to navigate around in the document, you can not only select child or descendant nodes of the context node but also its ancestors, siblings, its document node. If you want the `foo` child elements use the path `foo` or `./foo`, for the descendants use `.//foo` or `descendant::foo`. If you use `/` you select the document node of the context node and any path starting with `/` starts searching from the document node so `//id` selects all `id` descendants of the document node of the context node. – Martin Honnen Jan 21 '14 at 09:41
  • So when XPathSelectElement is called, the object upon which it is called simply sets the context node for your XPath expression; the XPath expression will still have access to the full document within which the context node exists. This makes sense - and I've learnt more about XPath - thanks. – rogersillito Jan 22 '14 at 11:56