Skip to content

XmlNodeReader MoveToAttribute bug, leaves XmlReader in inconsistent state #34443

@weltkante

Description

@weltkante

When using XmlNodeReader.MoveToAttribute returns false it leaves the XmlReader in an inconsistent state which can lead to follow-up exceptions.

Use-case: we are calling some deserialization API not owned by us which only accepts XmlReader. We already have an in-memory XmlDocument and need to call the deserialization method on a lot of nodes, so we want to use XmlNodeReader instead of doing an unnecessary format/parse roundtrip through an intermediate string for every node we have to deserialize.

In some cases the code calls MoveToAttribute to detect whether an optional attribute is present. This part works, but when it then subsequently calls ReadStartElement this leads to an exception. See attached example which reduces the call sequence to present the problem.

The error happens on both Desktop Framework and .NET Core.

using System;
using System.IO;
using System.Xml;

class Program
{
    static void Main(string[] args)
    {
        var doc = new XmlDocument();
        doc.LoadXml("<root><child attr1='value1'><other /></child></root>");

        Console.WriteLine("Testing XmlReader.Create");
        using (var rd = XmlReader.Create(new StringReader(doc.DocumentElement.FirstChild.OuterXml)))
            RunTest(rd);
        Console.WriteLine();

        Console.WriteLine("Testing XmlTextReader");
        using (var rd = new XmlTextReader(new StringReader(doc.DocumentElement.FirstChild.OuterXml)))
            RunTest(rd);
        Console.WriteLine();

        Console.WriteLine("Testing XmlNodeReader");
        using (var rd = new XmlNodeReader(doc.DocumentElement.FirstChild))
            RunTest(rd);
        Console.WriteLine();
    }

    static void RunTest(XmlReader rd)
    {
        Console.WriteLine($"  reader state: {rd.NodeType} {rd.Name}");
        Console.WriteLine($"IsStartElement(child) = {rd.IsStartElement("child")}");
        Console.WriteLine($"  reader state: {rd.NodeType} {rd.Name}");
        Console.WriteLine($"MoveToAttribute(attr1) = {rd.MoveToAttribute("attr1")}");
        Console.WriteLine($"  reader state: {rd.NodeType} {rd.Name}");
        Console.WriteLine($"MoveToAttribute(attr2) = {rd.MoveToAttribute("attr2")}");
        Console.WriteLine($"  reader state: {rd.NodeType} {rd.Name}");
        try
        {
            rd.ReadStartElement("child");
            Console.WriteLine($"ReadStartElement succeeded");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"ReadStartElement failed: {ex.Message}");
        }
    }
}
Testing XmlReader.Create
  reader state: None
IsStartElement(child) = True
  reader state: Element child
MoveToAttribute(attr1) = True
  reader state: Attribute attr1
MoveToAttribute(attr2) = False
  reader state: Attribute attr1
ReadStartElement succeeded

Testing XmlTextReader
  reader state: None
IsStartElement(child) = True
  reader state: Element child
MoveToAttribute(attr1) = True
  reader state: Attribute attr1
MoveToAttribute(attr2) = False
  reader state: Attribute attr1
ReadStartElement succeeded

Testing XmlNodeReader
  reader state: None
IsStartElement(child) = True
  reader state: Element child
MoveToAttribute(attr1) = True
  reader state: Attribute attr1
MoveToAttribute(attr2) = False
  reader state: Element attr1
ReadStartElement failed: Element 'child' was not found.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-System.Xmlbugin-prThere is an active PR which will close this issue when it is merged

    Type

    No type

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions