【问题标题】:XML Files which are serialized using XmlTextWriter and then read using XmlTextReader are sometimes corrupted使用 XmlTextWriter 序列化然后使用 XmlTextReader 读取的 XML 文件有时会损坏
【发布时间】:2011-09-11 16:08:10
【问题描述】:

我们有一个产品,它在 C# - .NET 2.0 中使用“XmlTextWriter”来创建大量的小型 XML 文件。然后使用“XmlTextReader”重复读取这些文件。

我们发现,在极少数客户机器上,XML 文件的内容会被大量空格替换。一旦发生这种情况,“XmlTextReader”显然将无法读取 XML 文件,并出现错误“缺少根元素”。

这里是逻辑细节:

  1. 当写入一个新的 Xml 文件时 - 文件首先被写入一个临时文件夹,使用:

    XmlTextWriter xDoc = new XmlTextWriter(Path, Encoding.UTF8);
    
  2. 将文件写入临时文件夹后 - 'XmlTextReader' 用于验证文件。

  3. 当且仅当文件经过验证时,才会将其复制到最终位置。

  4. 几天之内,该文件被多次读取:

    XmlTextReader xDoc = new XmlTextReader(Path);
    
  5. 在极少数情况下,读取器失败并显示错误“缺少根元素”,我们看到 XML 文件现在包含许多空格且没有 XML 数据。

以下是一些代码摘录:

此代码用于序列化。
(请记住,序列化是在临时文件夹中完成的,并且只有在验证了临时 XmlFile 后才会复制到最终位置。)

            public void SerializeWithXmlTextWriter(XMLMetaData instance, string Path)
    {
        instance.CommitLists();

        #region XmlTextWriter

        XmlTextWriter xDoc = null;

        try
        {
            xDoc = new XmlTextWriter(Path, Encoding.UTF8);
            xDoc.Formatting = Formatting.Indented;

            xDoc.WriteProcessingInstruction("xml", "version=\"1.0\" encoding=\"utf-8\"");
            xDoc.WriteStartElement("MD");
            xDoc.WriteAttributeString("xmlns", "xsi", null, "http://www.w3.org/2001/XMLSchema-instance");
            xDoc.WriteAttributeString("xmlns", "xsd", null, "http://www.w3.org/2001/XMLSchema");


// A number of other elements are written here

            xDoc.WriteEndElement();
        }
        finally
        {
            if (xDoc != null)
            {
                xDoc.Close();
                xDoc = null;
            }
        }

        #endregion
    }

此代码用于反序列化。
(也用于序列化后的文件校验)

            public XMLMetaData DeserializeWithXmlTextReader(string Path)
    {
        XMLMetaData instance = new XMLMetaData();

        #region XmlTextReader

        XmlTextReader xDoc = null;

        try
        {
            xDoc = new XmlTextReader(Path);



            while (xDoc.Read())
            {
                switch (xDoc.Name)
                {
                    //Each element is processed using a case statement
                    //Omitted from this code sample
                }
            }
        }
        finally
        {
            if (xDoc != null)
            {
                xDoc.Close();
                xDoc = null;
            }
        }

        #endregion

        return instance;
    }

几个月来,我们一直在努力解决这个问题,但无法取得任何进展,因为它只发生在数千台客户端计算机上。我们从未能够在我们的开发和测试机器上复制它。

我们收到了关于备份应用程序停止时问题消失的报告。我们还有一位客户似乎只在运行 Visual Studio 时遇到问题。

也适用于有此问题的客户 - 似乎每隔几周才会发生一次!

提前感谢您的帮助:)

西蒙

【问题讨论】:

  • 您可能会注意到,我在 Close() XmlTextWriter 之前没有调用 Flush()。如果您查看 MSDN 上的示例 - 似乎不需要调用 Flush()。 link
  • 不要假设 MSDN 代码示例展示了最佳实践,甚至可以正常工作。我曾经花时间调试之前从 MSDN 复制粘贴的代码。
  • @Peter - 是的,我完全同意你的看法。如果您查看此链接link,似乎只有在您不调用 Close() 时才需要 Flush()。我将尝试使用 Flush() 进行一些测试。问题是在文件复制发生之前文件的内容是正确的。后来文件的内容被破坏了!此外,目前的代码仅对我们 5000 多个用户中的一小部分失败。
  • 您永远不应该使用new XmlTextReader()new XmlTextWriter()。自 .NET 2.0 以来,这些已被弃用。请改用XmlReader.Create()XmlWriter.Create()
  • @John...希望该类在使用 VS 时无需我寻找它就表明它已被弃用。会为我节省很多浪费的时间!

标签: c# xml


【解决方案1】:

至少自从我们切换到 .Net 2.0 以来,我们也遇到了同样的问题。

我们使用“XmlTextWriter”和 ISO-8859-1 编码写入临时文件,然后复制它。 我们得到一个空文件,大小为 0。

我们在关闭前使用 Flush,但这也不起作用。

这种情况很少发生,我们有大约 4000 个用户,大约每月发生一次。出的假设是存在一个不给出异常的内部错误。

我们使用该文件进行设置,因此我们当前的解决方法是在读取时遇到此问题时生成默认值。

我们也在使用Formatting.Indented,也许这就是罪魁祸首。

我们的代码:

    Public Sub Save(ByVal st As Stream, Optional ByVal AttachComment As Boolean = True)

    Dim xtw As New XmlTextWriter(st, Text.Encoding.GetEncoding("ISO-8859-1"))

    xtw.Formatting = Formatting.Indented

    xtw.WriteStartDocument()

    'Header.
    If AttachComment Then
        xtw.WriteComment(GenerateCreationComment())
    End If

    xtw.WriteStartElement("SektionsdataFile")

    xtw.WriteStartElement("Header")
    WriteStringElement(xtw, "FileType", "Settings")
    WriteStringElement(xtw, "FormatVersion", CurrentFormatVersion)
    xtw.WriteEndElement()

    'Settings.
    xtw.WriteStartElement("Settings")

    SaveSettingsCategory(xtw, Application)
    SaveSettingsCategory(xtw, Behaviour)
    SaveSettingsCategory(xtw, Calculation)
    SaveSettingsCategory(xtw, Forms)
    SaveSettingsCategory(xtw, Hardware)
    SaveSettingsCategory(xtw, Layout)
    SaveSettingsCategory(xtw, License)
    SaveSettingsCategory(xtw, Paths)
    SaveSettingsCategory(xtw, Printing)

    xtw.WriteEndElement()

    xtw.WriteEndElement()

    xtw.WriteEndDocument()

    xtw.Flush()

    xtw.Close()

End Sub

【讨论】:

  • @AmnisJonas - 感谢您确认此问题也发生在其他开发人员身上。我还认为 .NET 2.0 中存在某种错误,其中 user.config 设置文件以类似的方式损坏。我认为这个问题仅限于 XmlSerializer 类,但似乎不是。我们在多个产品中遇到过很多情况,其中 user.config 设置文件已损坏,其内容仅包含许多空格。
  • 自我发布此问题以来已经过去一年 - 我们仍然遇到一些 XML 序列化和损坏问题。你有什么运气吗?如果你能帮助我们,那就太好了。
【解决方案2】:

您声明该文件经过验证,然后被复制,并且经过验证的文件稍后会中断。我至少可以看到四种可能性:

  1. 比赛条件。出现此问题的原因是您在复制新版本以替换它的同时读取文件。
  2. 复制过程出了问题。
  3. 文件已成功复制,随后被其他进程清空。
  4. 文件已成功复制,后来在您读取配置时被 XmlTextReader 中的错误清除。

解决方案 1 是使用跨进程同步 - 例如信号量。

调查2,复制后可以查看文件。

对于 3 和 4,您可以确保以只读方式创建副本,对于 4,您可以向 XmlTextReader 传递以只读方式打开的 FileStream 而不是路径。

如果这些都没有帮助,至少你已经排除了一些可能性。

【讨论】:

  • 1 和 2 是不可能的。我将实施代码来防止 3 和 4。我怀疑第 4 点是原因。谢谢。
【解决方案3】:

首先使用

using(XmlWriter xmlw = XmlWriter.Create(String, XmlWriterSettings))
{
  // your code here
}

作为创建 xml 文档的样板。试试看,如果它也失败了就回来(我会很惊讶)。还可以使用 using 构造来读取 XML。

XmlTextReader 的用法是 not recommended by Microsoft(见注释)。

【讨论】:

  • 感谢您的指点。我将按照您的建议修改代码。我们需要一段时间才能验证问题是否已解决,因为它很少被复制。
  • 这对我也不起作用。似乎在使用 XMLTextWriter 编写 XML 的紧凑框架中存在错误。我不得不回到写文件的旧方式...使用 (FileStream filestream = new FileStream(ConfigFile,FileMode.Truncate)) {XmlDocument doc = new XmlDocument();字符串文本 = doc.InnerXml; filestream.Write(System.Text.Encoding.UTF8.GetBytes(text),0, text.Length);文件流.Dispose();}
猜你喜欢
  • 2012-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多