-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
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.