It's not possible to do this without making some significant assumptions about the nature of your text. Most notably, you have to assume that it's well-formed XML, and that it contains neither CDATA sections nor namespaces.
If you start at any position in the middle of a stream and back up until you hit what appears to be the start of an element, you have no way of knowing that the text you're looking at actually is the start of an element. It could be CDATA. And you can't tell that it's not CDATA until you've backtracked through the entire stream looking for <![CDATA[
and haven't found it.
Namespaces present a similar problem. If you find a start tag like <Foo
, you can't know for certain that Foo
is in the default namespace until you've backtracked all the way to the document's root element and ascertained that no ancestor element has a namespace declaration. If you find <x:Foo
, you have to backtrack until you find an enclosing element with an xmlns:x
declaration.
If you know for sure that the text is well-formed XML, that it doesn't contain CDATA, and that its use of namespaces is limited (i.e. you can tell what namespace an element is in just by looking at its start tag), then some of what you're trying to do is at least possible.
You can back up to the first start tag you encounter, create a StreamReader
whose origin is that position, and use that to create an XPathDocument
that's set up to handle document fragments. Note, by the way, that you have no assurance that the XPathDocument
won't read all the way to the end of the text the first time you use it unless, again, you have knowledge about the nature of the text and you know that the matching end tag is going to be present.
But this won't handle the specific case you mentioned, i.e. finding the parent element. To find the parent element you'd need to find a start tag that isn't preceded (as you move backwards) by a matching end tag. This isn't terribly difficult to do - every <
character you find is going to be the beginning of either a start tag, an end tag, or an empty element, and you can just put end tags on a stack and pop them off when you find their matching start tag. When you hit a start tag and the stack is empty, you're at the start of the parent element.
But this too is a process that might result in your backtracking all the way to the stream's origin, especially in the trivial case where the XML you're looking is the classically moronic XML log format:
<log>
<entry>...</entry>
<entry>...</entry>
...repeated ad infinitum