|
dev
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
XmlBookmarkReader bug using ReadSubtreebut not necessarily supported by Microsoft). The article is found here: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnxmlnet/html/XmlBkMkRead.asp Consider the following code to print xml. This code assumes that the xml text given in the reader will be of a certain form -- basically having a root node, which is not a crazy assumption at all. It just makes the code simpler to demonstrate the bug. public static void PrintXml(XmlReader reader) { bool inElement = false; string elementName=""; XmlBookmarkReader bReader = new XmlBookmarkReader(reader); bReader.Read(); if (bReader.HasAttributes) { Console.Write("<"+bReader.Name); for (int i=0; i < bReader.AttributeCount; i++) { Console.Write(" "); bReader.MoveToAttribute(i); Console.Write("{0}=\"{1}\"", bReader.Name, bReader.Value); } bReader.MoveToElement(); Console.WriteLine(">"); } else { Console.WriteLine("<"+bReader.Name+">"); } while (bReader.Read()) { switch (reader.NodeType) { case XmlNodeType.Element: if (inElement) { bReader.ReturnToBookmark(elementName); PrintXml(bReader.ReadSubtree()); inElement = false; } else { inElement = true; elementName = bReader.Name; bReader.SetBookmark(elementName); if (!bReader.HasAttributes) Console.Write("<" + bReader.Name + ">"); else { Console.Write("<" + bReader.Name); for (int i = 0; i < bReader.AttributeCount; i++) { Console.Write(" "); bReader.MoveToAttribute(i); Console.Write("{0}=\"{1}\"", bReader.Name, bReader.Value); } bReader.MoveToElement(); Console.Write(">"); } } break; case XmlNodeType.EndElement: Console.WriteLine("</" + bReader.Name + ">"); inElement = false; bReader.RemoveBookmark(elementName); break; case XmlNodeType.Text: Console.Write(bReader.Value); break; } } reader.Close(); } Now pass the code above this XML text: <Config> <Id attrib="false">12345-6789-12364</Id> <DisplayName attrib="true">Computer</DisplayName> <ServerList count="4"> <Server0 checkedin="true">https://web1.com</Server0> <Server1 checkedin="false">https://web2.com</Server1> <Server2 checkedin="false">https://web3.com</Server2> <Server3 checkedin="false">https://web4.com</Server3> </ServerList> </Config> Code to turn that text into an XmlReader: string xml = [XML text above]; XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable()); nsmgr.AddNamespace("", ""); XmlParserContext parseContext = new XmlParserContext(null, nsmgr, null, XmlSpace.None); XmlTextReader reader = new XmlTextReader(xml, XmlNodeType.Document, parseContext); reader.WhitespaceHandling = WhitespaceHandling.None; PrintXml(reader); You get this result: <Config> <Id attrib="false">12345-6789-12364</Id> <DisplayName attrib="true">Computer</DisplayName> <ServerList count="4"><ServerList count="4"> <Server0 checkedin="true">https://web1.com</Server0> <Server0 checkedin="true">https://web2.com</Server1> <Server0 checkedin="true">https://web3.com</Server2> <Server0 checkedin="true">https://web4.com</Server3> </ServerList> </Config> Notice the Server1, Server2 and Server3 start elements all are Server0, but the end elements are correct. Notice they all share the same 'checkedin' attribute values as Server0 as well. ServerList appearing twice is a side effect of having to go back using the bookmark. That can be dealt with, but for simplicity, I didn't bother to correct it here. I've pinned this bug down to an issue with XmlBookmarkReader and ReadSubtree. There are some strange effects going on that I don't understand. For example, removing the "checkedin="true"" from the Server0 element in the original XML doesn't result in this behavior. You get the results you would expect. In fact, adding any non-attributed element before Server0 gives you the results you would expect even an empty element. The only bit of insight I have with this problem is that the original reader, before the recursive call to PrintXml, is positioned on Server0 while the bookmark reader is positioned on the cached ServerList node when the call to ReadSubtree is executed. Any help pinning down the problem would be greatly appreciated. My conclusion is that a call to ReadSubtree on CachedXmlNode in the XmlBookmarkReader may have unexpected results," but I would still like to solve this problem. A solution, though not as elegant, is to forget about using
ReadSubtree. Instead you assume either the first node read or the current node is the root node and you read until you encouter the EndElement with the same name as the root node. You call PrintXml recursively like above, but you must remove all bookmarks (because you're working with the same XmlBookmarkReader). It works, but has a lot more potential for error. If anyone is interested, I can post the code. |
|||||||||||||||||||||||