关于C# XML序列化的一个BUG的修改

原文:关于C# XML序列化的一个BUG的修改

关于C# XML序列化的一个BUG的修改

在我前一篇博客中提到用XML序列化作为数据库的一个方案,@拿笔小心 提到他们在用XML序列化时,遇到了一个比较严重的bug,即XML不闭合,系统不能正确的加载此XML。在我的开发经验中,也遇到过这样的问题。现在把这个BUG的描述及解决方案记录如下,也供遇到此BUG的朋友参考。

BUG描述

这个BUG的出现也是比较诡异的,我们给客户做的一套系统,这个系统会把数据写到N个xml文件中,正常情况下都没有问题。直到有一天……客户运行程序运行了一天,到快下班的时候,把数据保存到数据库中;第二天来上班时,忽然发现数据都没有了,也就是说昨天一天的工作白做了。

当客户把这个BUG告诉我的时候,我第一时间的反应是要重现这个BUG。因为同样的系统N份已经运行了一年了,从来没有出现过这个问题。结果客户在同样的机器上再次测试,没有遇到这个问题。我以为这个BUG是偶然现象,就没有处理,结果噩梦开始了。

当客户把系统部署到生产系统中之后,生产系统中偶尔也出现这个问题,每次出现这个问题,基本上耽误了一天的工作,损失都是N万,当时压力巨大,赶紧扎到现场解决问题。

我发现之所以以前没有出现这个BUG,是因为以前的数据量都非常少,但这个版本的数据量很大。我观察了数据文件,发现是XML文件丢失了一部分结尾造成的。如丢失一个>号,导致不能正确加载XML,数据丢失。

解决方案1

既然定位到了问题,那就有解决方案了。每次写完数据之后,我都会重新LOAD一下数据以验证正确性,如果不正确,则重新写入数据。用这个思路,我很快改了一版,部署到生产环境中。(测试环境很难重新这个错误)。

然而,问题还是没有解决,一个月之后,同样的问题又出现了。看来必须找到根本原因,不能取巧解决问题。

解决方案2

我开始google这个问题,最后在stackoverflow上找到:xdocument save adding extra characters。他的描述是XML会增加字符,我的问题是会减少字符。

原来XML的写入方式是:

config.Save(new FileStream(@"c:\foo.xml", FileMode.Create, FileAccess.Write), SaveOptions.None);

应该改为

using (FileStream fs = new FileStream(@"C:\foo.xml", FileMode.Truncate, FileAccess.Read))
{
    config.Load(fs);
}

主要修改是把FileMode.Create改为FileMode.Truncate。

Use FileMode.Truncate in your write FileStream so that the file is truncated to 0 bytes before you start writing to it.

这个方案看起来是靠谱的,然而,我还是不放心。

解决方案3

为了防止再次出问题,我写了一个FixErrorXmlFile方法,解决去除xml多字符的问题,在每次加载xml的时候,如果出现错误,调用此方法修正xml错误。其核心代码如下:

  private static bool ReadFile(string filePath, out string realContent)
        {
            string content = string.Empty;
            realContent = string.Empty;
            using (FileStream fs = new FileStream(filePath, FileMode.Truncate))
            {
                using (StreamReader sr = new StreamReader(fs))
                {
                    content = sr.ReadToEnd();
                }
            }
            //首先,要找到文件头末尾的'>'(即第一个右尖括号)的索引值index1,如果index1的值小于1,说明'>'不存在,跳出:否则往下执行
            //然后,找到根元素左侧的'<'的索引值index2,同样,如果'<'存在继续往下执行
            //      找到根元素右侧的第一个'>'的索引值index3和第一个' '的索引值index4
            //      比较index3和index4,较小者为根元素右侧的第一个元素的索引
            //      找出根元素的名称
            //接着,找到最后一个匹配根元素名称的开始位置index5
            //最后,确定根元素右侧第一个'>'的索引值,来获取文件的真正内容realContent
            int index1 = content.IndexOf('>');
            if (index1 < 1)
            {
                return false;
            }
            int index2 = content.IndexOf("<", index1);
            if (index2 < 1)
            {
                return false;
            }
            int index3 = content.IndexOf(">", index2);
            int index4 = content.IndexOf(" ", index2);
            int index = index3 < index4 ? index3 : index4;
            string rootName = content.Substring(index2 + 1, index - index2 - 1);
            int index5 = content.LastIndexOf(rootName);
            if (index5 < 1)
            {
                return false;
            }
            index5 += rootName.Length;
            realContent = content.Substring(0, index5 + 1);
            return true;
        }

后记

我同时把解决方案2和解决方案3都修改了,再次放到了生产系统中。一年过去了,再也没有出现过这个BUG。这个一年指的是同时5台机器一直不停的运行。

后来我又观察了一下,好像我的解决方案3根本就没起作用,从来没有进入过这个函数。也就是说,解决方案2已经解决了这个问题。

虽然这个问题没有重现,但我还是不认为这个问题已经完美解决。我认为这是微软的一个BUG,在正常应用序列化的情况下会出现丢失数据,应该由微软来解决,而不是采用其它的补丁方式解决问题。微软类似的BUG遇到好几个了。

总之,这个问题就是这样解决了,希望对遇到相似问题的人有所帮助。也欢迎大家指出我的问题。

参考:

http://www.cnblogs.com/wardensky/p/4170605.html

我同事当时记录的这个问题:xml存储bug

时间: 2024-09-20 10:57:49

关于C# XML序列化的一个BUG的修改的相关文章

如何在.net 当中XML序列化一个Collection

Collection主要是指像Array, ArrayList, List, Dictionary, HashTable这 些数据类型,大家平时用的很多.如果一个类中有一个Collection类型的成员, 在对这个类进行XML序列化的时候,应该如何处理?应该说在.net当中这是比较 简单的,只要建立一个XmlSerializer类就可以帮你自动搞定,不过有的时候你 可能需要对自动的序列化过程施加更多的控制,比如XML的结构是实现固定的, 你必须按照要求去生成XML结构. 使用不同的属性可以灵活的

请救xml序列化对象,对象类中有一个public byte数组,怎么才能让他不被序列化

问题描述 请救xml序列化对象,对象类中有一个publicbyte数组,怎么才能让他不被序列化,按原字节输出如下<packge><id><bytes></bytes</packgebytes里放的是图片,我将图片二进制流放入进去,不想被序列化,因为客户端是c语言不能反序列化,怎么做呢. 解决方案 解决方案二:那就都不要序列化,自己按按结构写到流里面,对于流什么语言都是通用的解决方案三:引用1楼bdmh的回复: 那就都不要序列化,自己按按结构写到流里面,对于流

做一个通用的XML序列化,支持所有类型

小知识:typeof(类型名)和实例.GetType()是什么? typeof(类名):返回直指的System.Type对象,并可以通过Type对象访问基类及本类一些信息 GetType():是object类下实例方法,即无论是自己定义的类还是.net框架类都可以用此方法 如果想返回String类型的全类型形式,可以用typeof(String) 如果想返回对象string a=null;中实例a的类型,可以用a.GetType() #region XML序列化 public static vo

使用Simple简化XML序列化

Simple 是什么? Simple 是一个 Java 框架,用于简化序列化和反序 列化 XML 的过程.使用 Simple,开发人员可以简化(名字由此而来)将简单老 Java 对象(POJO)转换成 XML 文档的过程 - 即所谓的序列化 (serialization)过程.Simple 也可促进相反的过程:开发人员可以将 XML 文 档转换成 POJO - 即所谓的反序列化(deserialization)过程. Simple 名副其实,它使用注解来支持序列化和反序列化过程.根据相应的 XM

利用XML序列化实现程序配置文件

 有些应用程序在退出的时候,会将一些设置值写入到文件里,以便下次程序启动时调用,这个文件统称为配置文件.例如:Windows的扫雷程序,在每次启动的时候,都会出现在上次关闭的位置,就是因为扫雷程序在退出的时候将当前位置写入到配置文件里. 早期的配置文件的实现有两种方法.一是INI文件,在Win32的API中还专门有读写INI文件的API函数:另一个是注册表,这也是很多程序的首选,在版本较新的扫雷程序就是利用注册表实现配置函数.不过,上述的两种方法都有其的局限性.INI文件,结构简单,容易编辑,有

Asp.Net Web API 2第十三课——ASP.NET Web API中的JSON和XML序列化

原文:Asp.Net Web API 2第十三课--ASP.NET Web API中的JSON和XML序列化 前言 阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok/p/3446289.html 本文描述ASP.NET Web API中的JSON和XML格式化器. 在ASP.NET Web API中,媒体类型格式化器(Media-type Formatter)是一种能够做以下工作的对象: 从HTTP消息体读取

【ASP.NET Web API教程】6.2 ASP.NET Web API中的JSON和XML序列化

原文:[ASP.NET Web API教程]6.2 ASP.NET Web API中的JSON和XML序列化 谨以此文感谢关注此系列文章的园友!前段时间本以为此系列文章已没多少人关注,而不打算继续下去了.因为文章贴出来之后,看的人似乎不多,也很少有人对这些文章发表评论,而且几乎无人给予"推荐".但前几天有人询问为何很久没有更新,这让我感觉把这文章翻译出来还是有价值的.为此,本人打算将此工作继续下去.这些关于Web API的技术文章均由微软专业人员撰写,虽然文章作为博客帖子而写得比较简单

.NET对象的XML序列化和反序列化

 序列化的概念 序列化是指一个对象的实例可以被保存,保存成一个二进制串,当然,一旦被保存成二进制串,那么也可以保存成文本串了.比如,一个计数器,数值为2,我们可以用字符串"2"表示.如果有个对象,叫做connter,当前值为2,那么可以序列化成"2",反向的,也可以从"2"得到值为2的计数器实例.这样,关机时序列化它,开机时反序列化它,每次开机都是延续的.不会都是从头开始. 序列化概念的提出和实现,可以使我们的应用程序的设置信息保存和读取更加方便

XmlSerializer 对象的Xml序列化和反序列化

try { XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("my", "http://flibble"); Workers workers = new Workers(); workers.MyWorkers = new Worker[] { new Worker() { Name = "1", Number = 1 }, new Worker() { Nam