我正在把头发拉出来。我使用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
位于 字符串 的起始元素上节点。跳过 背景 元素!为什么呢?
当reader
为XmlTextReader
并且WhitespaceHandling
属性设置为WhitespaceHandling.None
或WhitespaceHandling.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
尚未检查它是否正确定位并且“意外”将文档读取到最后。但这不是这个问题的范围。
答案 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中描述的一些意外行为。
XmlReader
与WhietespaceHandling.All
和WhietespaceHandling.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,因此我无法使用此方法。