当WhitespaceHandling设置为None或Significant时,为什么XmlTextReader正在跳过节点

时间:2012-04-05 18:41:15

标签: c# xml-serialization .net-2.0

我正在把头发拉出来。我使用XmlReader进行一些手动反序列化 - 没有什么严重的,zilion时代完成了。但这是我无法弄清楚的。

这是示例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位于 缩略图 节点的结束元素上。然后调用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

[编辑2] 调整后的调试输出更易读。

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

1 个答案:

答案 0 :(得分: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 ,则读者可能会表现得好像消耗了第一个内容,然后调用了Read方法。这意味着读者将读取所有文本,直到遇到end元素。然后它将读取结束标记节点,读取下一个节点,然后将其自身定位在下一个后续节点上。

似乎尽管读到元素内容的末尾(我知道数据长度所以理论上我可以这样做),XmlReader并没有考虑到我“消耗”了所有元素的内容。这导致了MSDN中描述的一些意外行为。

XmlReaderWhietespaceHandling.AllWhietespaceHandling.Significant的行为相同。我的代码与WhietespaceHandling.All一起使用,因为在最后一次调用XmlReader.ReadElementContentAsBase64之后,reader正在跳过非重要的空格。如果source 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 base,因此我无法使用此方法。