关于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
关于C# XML序列化的一个BUG的修改的更多相关文章
- C#实现接口xml序列化与反序列化
C#实现接口xml序列化与反序列化 C#中接口无法被xml序列化,提示不支持.百度和bing也搜不到,只好自己动手写了 原理上肯定支持,.Net自己的xml序列化有一个IXmlSerializab ...
- C# 二进制序列化(BinaryFormatter),Xml序列化(XmlSerializer),自己模拟写一个Xml序列化过程。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...
- [.net 面向对象程序设计进阶] (11) 序列化(Serialization)(三) 通过接口 IXmlSerializable 实现XML序列化 及 通用XML类
[.net 面向对象程序设计进阶] (11) 序列化(Serialization)(三) 通过接口 IXmlSerializable 实现XML序列化 及 通用XML类 本节导读:本节主要介绍通过序列 ...
- .NET中XML序列化的总结
[题外话] 以前虽然常用.NET中的序列化,但是常用的BinaryFormatter,也就是二进制文件的序列化,却鲜用XML的序列化.对于XML序列化,.NET中同样提供了一个非常方便的工具XmlSe ...
- XML序列化和反序列化
上篇总结了下JSON的序列化和反序列化,博园中大牛给了很多牛叉的评论,学习了不少. 不过在上篇中忘了把json序列化和反序列化的另外一种方式写上去了,这里做个简单的补充: Json篇:http://w ...
- 关于XML序列化与CultureInfo
不同的计算机系统可能有着不同的CultureInfo,例如在中文环境下日期通常这样显示03/30/2016,而在有的操作系统下它可能是这样的30.3.2016. 这样的话带来一个问题,例如在中文环境下 ...
- XmlSerializer 对象的Xml序列化和反序列化
http://www.cnblogs.com/yukaizhao/archive/2011/07/22/xml-serialization.html 这篇随笔对应的.Net命名空间是System.Xm ...
- Xml 序列化
1 XML序列化只能序列化对象的公有属性,并且要求对象有一个无参的构造方法,否者无法反序列化. 2 [Serializable]和[NonSerialized]特性对XML序列化无效!所以使用XML序 ...
- XML序列化与反序列化
public static class XmlHelper { private static void XmlSerializeInternal(Stream stream, object o, En ...
随机推荐
- Android权限安全(9)Android权限特点及权限管理服务AppOps Service
Android权限特点 权限管理服务AppOps Service 图中元素介绍: Ignore 是不提示的,Allow 是允许,Reject 是拒绝 Client是一个使用sms 的应用, AppOp ...
- rundeck email配置文件配置
最近工作中用到了一个任务管理软件rundeck,其中有个很重要的功能就是任务执行提醒,用邮件执行,其中一些配置项,官网没有详细的说明,在网上也没有一个整体的说明,在次跟大家共享下,rundeck的使用 ...
- varchar 保存英文中文区别。
varchar在SQL Server中是采用单字节来存储数据的,中文字符存储到SQL Server中会保存为两个字节,英文字符保存到数据库中,如果字段的类型为varchar,则只会占用一个字节,而如果 ...
- ArrayList和List之间的转换
开发中不免碰到List与数组类型之间的相互转换,举一个简单的例子: package test.test1; import java.util.ArrayList; import java.util.L ...
- linq 之左连接
List<ArticleModel> articleList = articleRepository.GetAllArticle(); List<UsersModel> use ...
- uva 11624 Fire!(搜索)
开始刷题啦= = 痛并快乐着,学到新东西的感觉其实比看那些无脑的小说.电视剧有意思多了 bfs裸体,关键是先把所有的着火点放入队列,分开一个一个做bfs会超时的 发现vis[][]是多余的,完全可以用 ...
- HDU 5303 Delicious Apples 美味苹果 (DP)
题意: 给一个长为L的环,起点在12点钟位置,其他位置上有一些苹果,每次带着一个能装k个苹果的篮子从起点出发去摘苹果,要将全部苹果运到起点需要走多少米? 思路: 无论哪处地方,只要苹果数超过k个,那么 ...
- DATAGUARD中手工处理日志v$archive_GAP的方法
从9i以后,oracle dataguard 备库一般都不需要手工处理丢失的日志,FAL自动会帮我们处理,下面通过个案例来讲下手工处理丢失的日志的方法: 1.在备库查询有哪些日志丢失,没应用到备库 S ...
- button捕捉回车键
为了给一个页面设置回车默认触发的按钮功能,我浏览了IE上的诸多方法,有的言不达意,有的读不懂,后来把高手的一段代码改造后,形成了一段代码,使这个问题的解决变得非常简章,有兴趣的朋友不妨一试.<s ...
- hdu 2155 小黑的镇魂曲(dp) 2008信息工程学院集训队——选拔赛
感觉蛮坑的一道题. 题意很像一个叫“是男人下100层”的游戏.不过多了个时间限制,要求在限定时间内从某一点下落到地面.还多了个最大下落高度,一次最多下落这么高,要不然会摔死. 一开始想dp的,然后想了 ...