3

How to get XPath for the current node with XMLReader?

E.g.:

<Employee>
    <Entity>
        <Id>1</Id>
    </Entity>
</Employee>

So I need to get XPath for 1 which is Employee/Entity/Id. Any ideas?

using (var reader = XmlReader.Create(basePath, settings))
{
    while (reader.Read())
    {                   
        if (reader.NodeType == XmlNodeType.Text)
        {
            // need to get xpath of the text node
        }
        else if (reader.NodeType == XmlNodeType.Element)
        {
            // need to get xpath of the current node
        }
     }
 }
Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
  • You'd have to keep track of what elements you're inside - you could use a stack and push each element as it's read and pop it when you reach EndElement. What are you trying to do, though? Do you need to drop down to `XmlReader`? This would be easier using a DOM like `XDocument`. – Charles Mager Jul 10 '15 at 10:51
  • @CharlesMager, Can you please provide some example ? –  Jul 10 '15 at 10:52
  • XmlReader can't use XPath. – Alexander Petrov Jul 10 '15 at 11:00
  • @AlexanderPetrov I need to iterate all nodes in a `Xml` and need to get `XPath` of each node? Is that can be done using `XDocument`? If so, Can you post some example ? –  Jul 10 '15 at 11:05

1 Answers1

8

My first suggestion would be to use a higher level API like LINQ to XML. The only reason to use a low level API like XmlReader is for extremely large files. With LINQ to XML, a naive implementation is fairly trivial:

var doc = XDocument.Parse(xml);

foreach (var element in doc.Descendants())
{
    var path = element.AncestorsAndSelf().Select(e => e.Name.LocalName).Reverse();
    var xPath = string.Join("/", path);
}

Using XmlReader is a bit more involved as you have to track the element path as you go:

using (var reader = XmlReader.Create(basePath, settings))
{
    var elements = new Stack<string>();

    while (reader.Read())
    {
        switch (reader.NodeType)
        {
            case XmlNodeType.Element:
                if(!reader.IsEmptyElement)
                    elements.Push(reader.LocalName);
                break;
            case XmlNodeType.EndElement:
                elements.Pop();
                break;
            case XmlNodeType.Text:
                path = string.Join("/", elements.Reverse());
                break;
        }
    }
}

Here's a working demo: https://dotnetfiddle.net/dpOzuL

Note that while this works for your trivial example, this is a very naive creation of an XPath expression and won't work in all cases (for example, when you have multiple siblings with the same name or when namespaces are involved).

Charles Mager
  • 25,735
  • 2
  • 35
  • 45
  • Thanks for your time, Is it possible to iterate all elements in a xml using xdocument and then take xpath? The code you posted will work only on the schema as per in the question –  Jul 10 '15 at 11:11
  • @SSS I've updated to iterate all elements. It's unclear what you're using this for. If you want a less naive implementation that would get you a valid XPath in all situations, then look at [this question](http://stackoverflow.com/questions/451950/get-the-xpath-to-an-xelement). – Charles Mager Jul 10 '15 at 11:16