I have an XML-Stream that contains the following XML content:
<WebError Key="A">
<Message>B</Message>
<Parameters>
<Parameter name="C">D</Parameter>
</Parameters>
</WebError>
I can't find a way to get an XmlReader
to read to the Key
attribute, so that reader.NodeType
is XmlNodeType.Attribute
and reader.LocalName
is "Key"
.
This is how I initialize my XmlReader
:
XmlReader.Create(stream, new XmlReaderSettings { CloseInput = true, IgnoreWhitespace = true });
This reader then is passed through several method levels, until it gets to my parser function.
Here is all the alternative code I tried to get the reader to read that element. Is stripped the control structures from the code, so you can only see the functions that are actually called.
First attempt, move to attributes by MoveToFirstAttribute()
call:
reader.Read(); // true
reader.IsStartElement("WebError"); // true
using (var nodeReader = reader.ReadSubtree()) {
nodeReader.HasAttributes; // true
nodeReader.MoveToFirstAttribute(); // false
nodeReader.Read(); // true
nodeReader.NodeType; // XmlNodeType.Element
nodeReader.LocalName; // "WebError"
using (var subLevelReader = nodeReader.ReadSubtree()) {
}
nodeReader.Read(); // false
}
So obviously, MoveToFirstAttribute
does not move the reader. As a side effect subLevelReader
that is usually used to parse inner XmlElement
nodes now grabs the whole WebError
node, and when subLevelReader
is disposed, the whole WebError
node is stepped over.
Second attempt, call MoveToContent()
and search for attributes:
reader.Read(); // true
reader.IsStartElement("WebError"); // true
using (var nodeReader = reader.ReadSubtree()) {
nodeReader.MoveToContent(); // XmlNodeType.Element
nodeReader.LocalName; // "WebError"
nodeReader.Read(); // true
nodeReader.NodeType; // XmlNodeType.Element
nodeReader.LocalName; // "Message"
...
}
Obviously I progressed too far already when I called MoveToContent()
because it moved to the end of the WebError
starting tag.
Third attempt, read attributes before calling MoveToContent()
:
reader.Read(); // true
reader.IsStartElement("WebError"); // true
using (var nodeReader = reader.ReadSubtree()) {
nodeReader.MoveToAttribute("Key"); // false
nodeReader.MoveToContent(); // XmlNodeType.Element
nodeReader.LocalName; // "WebError"
nodeReader.Read(); // true
nodeReader.NodeType; // XmlNodeType.Element
nodeReader.LocalName; // "Message"
...
}
This does not work either. So, how do I get to the WebError@Key
node?
This question (which unfortunately does not turn up in the search results for "xmlreader c# attribute") contains an answer which made me understand the problem: Read()
does not position the reader at an attribute. You first move to an element, then move to its content, then move to its attributes. Only this order works.
Turns out all my approaches work if you call MoveToContent()
before MoveToAttribute("Key")
, MoveToNextAttribute()
or MoveToFirstAttribute()
, but not Read()
yet because that reads to the Message
node.
So this is the actual code:
while (reader.Read()) {
if (!reader.IsStartElement("WebError")) { continue; }
// We found the WebError node
using (var nodeReader = reader.ReadSubtree()) {
nodeReader.MoveToContent();
// Read the attributes
while (nodeReader.MoveToNextAttribute()) {
var nodeName = nodeReader.LocalName;
if (nodeName == "Key") {
m_Key = nodeReader.Value; // "A"
break;
}
}
// Read the XML sub nodes
while (nodeReader.Read()) {
if (nodeReader.NodeType != XmlNodeType.Element) { continue; }
using (var subLevelReader = nodeReader.ReadSubtree()) {
// Parse sub levels of XML (Message, Parameters)
}
}
}
}