2012-04-05 58 views
1

我正在拉我的头发。我使用XmlReader进行一些手动反序列化 - 没有什么严重的,完成了zilion时间。但这是我无法弄清楚的。为什么在WhitespaceHandling设置为None或者显着时XmlTextReader跳过了节点

这是示例XML文件

<?xml version="1.0" encoding="utf-8"?> 
<Theme name="something" version="1.0.0.0"> 
    <Thumbnail length="1102">[some base64 encoded data] 
</Thumbnail> 
    <Backgrounds> 
    <string>Themes\something\Backgrounds\file1</string> 
    <string>Themes\something\Backgrounds\file2</string> 
    <string>Themes\something\Backgrounds\file3</string> 
    </Backgrounds> 
    <Stickers> 
    <string>Themes\something\Stickers\stick1</string> 
    <string>Themes\something\Stickers\stick1</string> 
    <string>Themes\something\Stickers\stick1</string> 
    </Stickers> 
    <PreviewImages> 
    <string>Themes\something\Preview\rh_01.jpg</string> 
    <string>Themes\something\Preview\rh_02.jpg</string> 
    <string>Themes\something\Preview\rh_03.jpg</string> 
    </PreviewImages> 
</Theme> 

这是反序列化码(一比特简化):

public void ReadXml(System.Xml.XmlReader reader) 
{  
    /* Read attributes - not important here */ 

    while (reader.Read()) 
    { 
     Console.WriteLine("Main: {0} {1}", reader.NodeType, reader.Name); 
     switch (reader.Name) 
     { 
      case Xml.Elements.Thumbnail: 
       this._thumbnail = Xml.DeserializeBitmap(reader); 
       Console.WriteLine("Inner: {0} {1}", reader.NodeType, reader.Name); 
       break; 
      case Xml.Elements.Backgrounds: 
       this._backgrounds = Xml.DeserializeListOfStrings(reader); 
       break; 
      case Xml.Elements.Stickers: 
       this._stickers = Xml.DeserializeListOfStrings(reader); 
       break; 
      case Xml.Elements.PreviewImages: 
       this._previewImages = Xml.DeserializeListOfStrings(reader); 
       break; 
     } 

     if (reader.NodeType == System.Xml.XmlNodeType.EndElement 
       && reader.Name == Xml.Root) 
      break; 
    } 
} 

问题:解串行化

this._thumbnail,所述reader是定位于的关闭元素T humbnail节点。然后reader.Read()while循环的开始处被调用...并且reader被定位在的起始元素上字符串节点。 背景元素被跳过!为什么?

这种情况发生在readerXmlTextReader,它的WhitespaceHandling属性设置为WhitespaceHandling.NoneWhitespaceHandling.Significant

如果它设置为WhitespaceHandling.All一切都按预期工作。在拨打reader.Read()后,reader定位在起始元素背景节点上。


[编辑]我已经添加了两个调试行于该示例的代码。

随着WhitespaceHandling.All我得到这样的:

Main: Whitespace 
Main: Element Thumbnail 
Inner: EndElement Thumbnail 
Main: Element Backgrounds 
Main: Whitespace 
Main: Element Stickers 
Main: Whitespace 
Main: Element PreviewImages 
Main: Whitespace 
Main: EndElement Theme 

WhitespaceHandling.Significant随着我得到这样的:

Main: Element Thumbnail 
Inner: EndElement Thumbnail 
Main: Element string 
Main: Text 
Main: EndElement string 
Main: Element string 
Main: Text 
Main: EndElement string 
Main: Element string 
Main: Text 
Main: EndElement string 
Main: EndElement Backgrounds 

[EDIT 2]调整调试输出一个位是更具有可读性。

如您所见,WhitespaceHandling.Significant的调试输出结尾为</Backgrounds>。这是因为我的Xml.DeserializeListOfStrings尚未检查它是否正确定位,并且“意外”将文档读取到最后。但这不是这个问题的范围。

+0

对于'WhitespaceHandling.All',奇怪的是,在Inner:EndElement Thumbnail和Main:Element Backgrounds之间没有Main:Whitespace行,因为XML中存在换行符。 – 2012-04-05 21:48:39

回答

0

我头疼的原因是XmlReader.ReadElementContentAsBase64方法,我用它来反序列化<Thumbnail>节点。

private static byte[] ReadBytes(System.Xml.XmlReader reader) 
{ 
    byte[] buffer = new byte[128]; 
    int length = XmlConvert.ToInt32(reader[Xml.Attributes.Length]); 

    using (MemoryStream ms = new MemoryStream(length)) 
    { 
     int count = 0; 

     do 
     { 
      count = reader.ReadElementContentAsBase64(buffer, 0, buffer.Length); 
      ms.Write(buffer, 0, count); 

     } while (ms.Length < length); 

     return ms.GetBuffer(); 
    } 
} 

但是MSDN说:

如果计数值比在文档中的字节数较高,或者如果它是等于多少,我在一个循环用它进行试验XmlNodeReader读取文档中的所有剩余字节,并返回读取的字节数。下一个ReadElementContentAsBase64方法调用返回一个零并将阅读器移动到EndElement节点之后的节点。

如果在消耗所有元素内容之前调用读取,读者可能会像第一个内容被消耗,然后调用Read方法那样工作。这意味着读者将读取所有文本,直到遇到end元素。然后它将读取结束标记节点,读取下一个节点,然后将其自身定位在下一个后续节点上。

看来,尽管阅读的元素内容结束(我知道数据长度所以理论上我可以做到这一点),该XmlReader没有考虑,我已经“消耗”的所有元素的内容。这导致了MSDN中描述的一些意外行为。

XmlReaderWhietespaceHandling.AllWhietespaceHandling.Significant表现相同。我的代码与WhietespaceHandling.All一起工作,因为在上次致电XmlReader.ReadElementContentAsBase64后,reader跳过了不显着的空白。如果源xml文件不包含换行符和制表符,那么我的代码也会失败,并且WhietespaceHandling.All也会失败。

解决的办法是修改while循环,在所有字节都为红色后再拨打XmlReader.ReadElementContentAsBase64。这种方法的缺点是,在该附加呼叫之后reader被移动到EndElement节点之后的节点。

do 
{ 
    count = reader.ReadElementContentAsBase64(buffer, 0, buffer.Length); 
    if (count > 0) 
     ms.Write(buffer, 0, count); 

} while (count > 0); 

人们还可以用XmlTextReader.ReadBase64方法来一次读取整个元素的内容,但我不得不为我的类实现IXmlSerializable的,所以这种方法不适用于我只使用XmlReader基地。

相关问题