关于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-12-11 12:10:12

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

xml序列化和解析

xml序列化和解析 XmlSerializer类的使用--XML序列化 首先获得一个XmlSerializer序列化对象 XmlSerializer serializer = new Xml.newSerializer(); 使用XmlSerializer生成一个XML文件: serializer.setOutput(aa.xml,"utf-8") serializer.startDocument("utf-8",true)//设置头文档serializer.sta

C#实现接口xml序列化与反序列化

C#中接口无法被xml序列化,提示不支持.百度和bing也搜不到,只好自己动手写了 原理上肯定支持,.Net自己的xml序列化有一个IXmlSerializable接口. 自行实现该接口,并取得实现改接口的类,再进行自定义的序列化处理即可. using System.Collections; using System.Xml.Serialization; using System; using System.Xml; [XmlRoot("SerInterface"), Serializ

使用XML序列化器生成XML文件和利用pull解析XML文件

首先,指定XML格式,我指定的XML格式如下: <?xml version='1.0' encoding='utf-8' standalone='yes' ?> <message> <sms> <body> 陈驰0 </body> <date> 1462162910995 </date> <address> 1380 </address> <type> 1 </type> &

Xml序列化和反序列化

读取Xml总共有三种方法:XmlTextReader   XmlDoucument   XPath 今天总结下XmlSerialize,xmlSerialize提供两个方法Deserialize(将xml反序列化成对象)和Serialize(将对象序列化城xml)   一.Serialize   序列化 /// <summary> /// transform target object in to xml file /// </summary> /// <typeparam

c# XML序列化与反序列化 属性字段标识

序列化对象 public class People { [XmlAttribute("NAME")] public string Name { set; get; } [XmlAttribute("AGE")] public int Age { set; get; } } [XmlRoot("Root")] public class Student : People { [XmlElement("CLASS")] public

Xml 序列化

1 XML序列化只能序列化对象的公有属性,并且要求对象有一个无参的构造方法,否者无法反序列化. 2 [Serializable]和[NonSerialized]特性对XML序列化无效!所以使用XML序列化时不需要对对象增加[Serializable]特性. XML形式 处理方法 补充说明 XmlElement 定义一个属性 属性名与节点名字匹配 XmlAttribute [XmlAttribute] 加到属性上 InnerText [InnerText] 加到属性上 一个类型只能使用一次 节点重

Android记录一个setTextColor常见的一个bug

今天写代码 一不小心就犯了个错误.仔细检查才发现,仅记录一下,防止各位同学犯同样的错误哦 代码如下: remote.setTextColor(summaryId, R.color.news_have_read); 如上,代码无论如何都不能正常显示颜色.只好换成如下代码: remote.setTextColor(summaryId,0xfdfdfdfd); 总不能写颜色值多麻烦..还是写的color.xml中吧. 最后才发现应该这样写才最对. remote.setTextColor(summary

XML序列化与反序列化+自定义XML注解框架XmlUtils

背景 前面一篇总结了Serializable的序列化与反序列化,现在接着总结XML.主要内容:XML基本的序列化与反序列化方法.一些注意事项.以及自定义了一个XML注解框架(简洁代码,解放双手). XML的序列化与反序列化 先与Serializable进行简单的对比: Serializable存储的文件,打开后无法正常查看,安全性高.xml文件可通过文本编辑器查看与编辑,可读性高(浏览器会格式化xml文件,更方便查看),安全性低: Serializable文件通过了签名,只能在自己的程序中反序列

[.net 面向对象程序设计进阶] (11) 序列化(Serialization)(三) 通过接口IXmlSerializable实现XML序列化及XML通用类

[.net 面向对象程序设计进阶] (11) 序列化(Serialization)(三) 通过接口IXmlSerializable实现XML序列化及XML通用类 本节导读:本节主要介绍通过序列化接口IXmlSerializable实现XML序列化和反序列化,整理了XML基础操作及序列化的通用类(包括XML及节点的基础读写操作,XML到DataSet\DataTable互转换操作,XML序列化及反序列化通用方法等). 读前必备: A.类和类的实例 [.net 面向对象编程基础]  (9) 类和类的