13

I got this scenario:

while (reader.Read())
{
    if (reader.NodeType == XmlNodeType.Element && reader.Name == itemElementName)
    {
        XElement item = null;
        try
        {
            item = XElement.ReadFrom(reader) as XElement;
        }
        catch (XmlException ex)
        {
           //log line number and stuff from XmlException class  
        }
    }
}

In the above loop I'm transforming a certain node (itemElementName) into an XElement.

Some nodes will be good XML and will go into an XElement, however, some will not.

In the CATCH, I'd like to not only catch the standard XmlException stuff... I'd also like to catch an extract of the current Xml and a string.

However, if I do any kind of READ operation on the node before I pass it to the XElement, it moves the reader forward.

How can get a "snapshot" of the contents of the OuterXml of the reader without interfering with it's position?

peterh
  • 11,875
  • 18
  • 85
  • 108
andy
  • 8,775
  • 13
  • 77
  • 122

6 Answers6

11

Actually ReadSubtree will return a reader which "wraps" the original reader. So reading through the new one will end up advancing the original one as well. You must consider XmlReader as a forward only reader, it simply can't go back. As for your scenario, instead of trying to remember part of the XML you can ask the reader for the position in the input file. Just cast it to IXmlLineInfo interface, it has methods to return line and position. Using this you could remember some starting position (before the element in question) and then the end position of the error. And then read that part from the intput file as a plain text.

Vitek Karas MSFT
  • 13,130
  • 1
  • 34
  • 30
  • yes, perfect, thank you, that's exactly what was happening. thanks for the explanation! – andy Jun 19 '10 at 00:09
  • Ah.. I totally misinterpreted the point of ReadSubTree then.. sorry about that. To be honest though, it doesn't make this obvious from the documentation in object browser, but the MSDN page makes it clearer. – Flynn1179 Jun 25 '10 at 08:30
  • 1
    I just went back and had another look at the code I've got that uses it; it actually stops reading altogether after it's finished with the subtree, so I never noticed it had advanced the original reader too. – Flynn1179 Jun 25 '10 at 08:31
5

Another idea: read the outer XML (which advances the reader), then create a new reader from this XML which allows you to "go back" and process the elements of the current node.

while (r.ReadToFollowing("ParentNode"))
{
    parentXml = r.ReadOuterXml();

    //since ReadOuterXml() advances the reader to the next parent node, create a new reader to read the remaining elements of the current parent
    XmlReader r2 = XmlReader.Create(new StringReader(parentXml));
    r2.ReadToFollowing("ChildNode");
    childValue = r2.ReadElementContentAsString();
    r2.Close();
}                  
CRD
  • 51
  • 1
  • 1
  • This worked very well for me, though I added 'using' blocks for the StringReader and the XmlReader. – gotorg Nov 20 '18 at 23:21
4

Don't use any 'Read' operation on the reader- as you've discovered, this advances it. Use calls to properties such as reader.HasValue and reader.Value to inspect the contents. Look up 'XmlReader' in the object browser, there's quite a few properties you can read.

Edit: I don't think there's an easy way of simply getting the XML, possibly because the current node may not be valid XML on it's own, such as an XmlWhiteSpace, XmlText node or even an XmlAttribute.

Flynn1179
  • 11,925
  • 6
  • 38
  • 74
  • thanks flynn... so for my purposes I'm hoping to get an extract of invalid xml before the error is thrown when trying to create the XElement. Are you saying that's impossible? – andy Jun 01 '10 at 10:57
  • 1
    Probably not impossible; I'm kicking myself for not remembering this before, but if you call `reader.ReadSubtree()`, it creates a whole new XmlReader that starts at wherever `reader` is; you can read that all you like without affecting your original `reader`. Not 100% sure how you'd use this in your situation, but it looks like it's probably the way to go. – Flynn1179 Jun 01 '10 at 12:33
1

What I did was read just the element into an XmlDocument, and read that instead. In my case, I had to convert a flow document to HTML. I had to read a inner element to assign a "style" to the parent HTML element.

0

In fact, although Vitek Karas MSFT is right, Helena Kupkova published a deft little XML Bookmark Reader on https://msdn.microsoft.com/en-us/library/aa302292.aspx. This enables going backwards using caching.

a_f
  • 36
  • 2
-1

Something like that with ReadSubtree

using (XmlReader reader = XmlReader.Create(new StringReader(xml)))
                                {
                                    reader.MoveToContent();
                                    while (reader.Read())
                                    {
                                        switch (reader.NodeType)
                                        {
                                            case XmlNodeType.Element:
                                                if (reader.Name == "Field") // for example i need to read node field
                                                {

                                                    XmlReader inner = reader.ReadSubtree();  // the reader stays in the same  position
                                                    XElement El = XElement.Load(inner) as XElement;
                                                    inner.Close();
                                                }
                                        }
                                    }
                                }
stef
  • 45
  • 6
  • Hi stef, thank you for answering questions on StackExchange! While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value. See also: https://meta.stackoverflow.com/questions/392712/explaining-entirely-code-based-answers – elaforma Jun 16 '20 at 10:39