[.net 面向对象程序设计进阶] (10) 序列化(Serialization)(二) 通过序列化博客园文章学习XML的序列化
上节我们介绍了二进制流的序列化,本节继续上节内容介绍XML序列化和反序列化。XML作为W3C标准数据传输格式,将XML对象化处理,认识和使用XML序列化类XmlSerializer,是.NET面向对象程序设计必须要掌握的知识。
A. 修饰符 [.net 面向对象编程基础 ] (8) 基础中的基础 —— 修饰符
B. 类和类的实例 [.net 面向对象编程基础 ] (9) 类和类的实例
C. 类的成员 [.net 面向对象编程基础 ] (10) 类的成员(字段、属性、方法)
D. 数组与集合 [.net 面向对象编程基础] (17) 数组与 集合
E. 泛型 [.net 面向对象编程基础] (18) 泛型
F.LINQ 使用 [.net 面向对象编程基础 ] (20) LINQ 使用
XML 指可扩展标记语言(EXtensible Markup Language)
XML 是一种标记语言,很类似 HTML
XML 的设计宗旨是传输数据,而非显示数据
XML 标签没有被预定义。您需要自行定义标签。
XML 被设计为具有自我描述性。
XML 是 W3C 的推荐标准
关于XML相关的基础知识,这里不作为重点赘述,下面入正题。
下面先来一个简单有意思的XML序列化,为了让小伙伴们有兴趣看完,我来实现动态获取博客园实时数据(这个不是重点,本节重在在于序列化和反序列化),进行序列化。
首先获取获博客园文章,创建文章实体类,如下:
public class MyBlogs { /// <summary> /// 获取我的博客园中文章 /// </summary> /// <returns></returns> public static List<MyArticle> GetMyArticle(int count) { var document = XDocument.Load( "http://wcf.open.cnblogs.com/blog/u/yubinfeng/posts/1/" + count ); List<MyArticle> myArticleList = new List<MyArticle>(); var elements = document.Root.Elements(); //在进行这个工作之前,我们先获取我博客中的文章列表 var result = elements.Where(m => m.Name.LocalName == "entry").Select(myArticle => new MyArticle { id = Convert.ToInt32(myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "id").Value), title = myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "title").Value, published = Convert.ToDateTime(myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "published").Value), updated = Convert.ToDateTime(myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "updated").Value), diggs = Convert.ToInt32(myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "diggs").Value), views = Convert.ToInt32(myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "views").Value), comments = Convert.ToInt32(myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "comments").Value), summary = myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "summary").Value, link = myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "link").Attribute("href").Value, author = myArticle.Elements().SingleOrDefault(x => x.Name.LocalName == "author").Elements().SingleOrDefault(x => x.Name.LocalName == "name").Value }).OrderByDescending(m=>m.published); myArticleList.AddRange(result); return myArticleList; } } /// <summary /// 我的博客文章实体类 /// </summary> public class MyArticle { /// <summary> /// 文章编号 /// </summary> public int id { get; set; } /// <summary> /// 文章标题 /// </summary> public string title { get; set; } /// <summary> /// 文章摘要 /// </summary> public string summary { get; set; } /// <summary> /// 发布时间 /// </summary> public DateTime published { get; set; } /// <summary> /// 最后更新时间 /// </summary> public DateTime updated { get; set; } /// <summary> /// URL地址 /// </summary> public string link { get; set; } /// <summary> /// 推荐数 /// </summary> public int diggs { get; set; } /// <summary> /// 浏览量 /// </summary> public int views { get; set; } /// <summary> /// 评论数 /// </summary> public int comments { get; set; } /// <summary> /// 作者 /// </summary> public string author { get; set; } }
下面实现myArticleList对象序列化XML并输出到控制台
//XML序列化命名空间:System.Xml.Serialization; //类:XmlSerializer //主要方法:Serialize和Deserialize //博客数据的对象化(反序列化)及序列化 //(1)获取我博客园中最后10篇文章 (实际上这个过程也是反序列化) List<MyArticle> myArticleList = MyBlogs.GetMyArticle(10); //(2)将上面的对象myArticleList序列化XML,并输出到控制台 string xmlString = String.Empty; using (MemoryStream ms = new MemoryStream()) { XmlSerializer xml = new XmlSerializer(typeof(List<MyArticle>)); xml.Serialize(ms, myArticleList); byte[] arr = ms.ToArray(); xmlString = Encoding.UTF8.GetString(arr, 0, arr.Length); ms.Close(); } Console.WriteLine(xmlString);
运行结果如下:
上面的示例,实现了一个最基本的从对象到XML的序列化。接下来完成上面示例序列化的xmlString文本对象化为List<MyArticle>
我们将上面序列化的文本xmlString反序列化为List<MyArticle>对象
//下面我们刚才输出的xmlString文本对象化(反序列化)为一个文章List对象 using (StringReader sr = new StringReader(xmlString)) { XmlSerializer xml=new XmlSerializer(typeof(List<MyArticle>)); ; List<MyArticle> myNewArticleList= xml.Deserialize(sr) as List<MyArticle>; //遍历输出反序化的新对象myNewArticleList myNewArticleList.ForEach(m => Console.WriteLine("文章编号:" + m.id + "/n文章标题:" + m.title) ); sr.Close(); }
运行结果如下:
上面的示例实现了从XML文本反序列化为List<MyArticle> 对象的过程。
学会了上面的两个示例,就掌握了XML序列化和反序列化的基本知识,下面介绍更进一步介绍在序列化过程中的一些细节。
(1)要序列化的类必须有默认的构造的构造函数,才能使用XmlSerializer序列化,需要序列化的类都必须有一个无参的构造函数(通过对基础中类和类的实例学习,我们必须知道类不定义构造函数的情况下,会默认生成一个无参数的构造函数);
(3)不想序列化时:当不想序列化一个属性时,使用[System.Xml.Serialization.XmlIgnore]标记,能用于属性;[NonSerializable]应用于属性无效,能用于类,结构体等;
(4)方法不能被序列化(虽然是废话,但是还是列举出来);
(5)枚举变量可序列化为字符串,无需用[XmlInclude]
(6)导出非基本类型对象,都必须用[XmlInclude]事先声明。该规则递归作用到子元素
(7)Attribute中的IsNullable参数若等于false,表示若元素为null则不显示该元素。(针对值类型有效)
(8)某些类就是无法XML序列化的(即使使用了[XmlInclude])
比如:
IDictionary(如HashTable);
父类对象赋予子类对象值的情况;
对象间循环引用;
(9)对于无法XML序列化的对象,可考虑:
使用自定义xml序列化(实现IXmlSerializable接口);
实现IDictionary的类,可考虑(1)用其它集合类替代;(2)用类封装之,并提供Add和this函数;
某些类型需要先经过转换,然后才能序列化为 XML。如XML序列化System.Drawing.Color,可先用ToArgb()将其转换为整数;
过于复杂的对象用xml序列化不便的话,可考虑用二进制序列化;
(10)默认构造函数是必须的,因为反序列化本质上使用的是反射,需要默认构造函数来实例化类,如果去掉其中的默认构造函数,则编译没有问题,但运行就会报错。
尽量不要将比较大的属性放在默认构造函数初始化,那会导致在反序列化时对列表初始化两次:默认构造函数中执行一次,反序列化时从XML文档读取再执行一次。
使用 XmlSerializerNamespaces类(System.Xml.Serialization.XmlSerializerNamespaces)。来完成
我们对上面最重得到的对象List<MyArticle>进行XML序列化,并更改XML默认的命名空间,示例如下:
//更新默认命名空间 //使用XmlSerializerNamespaces类来完成(System.Xml.Serialization.XmlSerializerNamespaces) System.Xml.Serialization.XmlSerializerNamespaces XmlSN = new XmlSerializerNamespaces(); //获取文章列表对象 List<MyArticle> newArticleList = MyBlogs.GetMyArticle(10); //更改命名空间,并输出 string newXmlString = String.Empty; XmlSN.Add("MyBlogURL", @"http://www.cnblogs.com/yubinfeng"); using (MemoryStream ms = new MemoryStream()) { XmlSerializer xml = new XmlSerializer(typeof(List<MyArticle>)); xml.Serialize(ms, newArticleList,XmlSN); byte[] arr = ms.ToArray(); newXmlString = Encoding.UTF8.GetString(arr, 0, arr.Length); ms.Close(); } Console.WriteLine(newXmlString);
我们可以和前面默认的输出结果比较,如下图:
也是可以删除命名空间的
System.Xml.Serialization.XmlSerializerNamespaces XmlSN = new XmlSerializerNamespaces(); XmlSN.Add("","");
我们先列出XmlWriterSettings可以更改的属性。
XmlWriterSettings 更多设置属性如下:
成员 | 说明 |
CloseOutput | 获取或设置一个值,该值指示在调用 Close 方法时, XmlWriter 是否还应该关闭基础流或 TextWriter 。 |
Encoding | 获取或设置要使用的文本编码的类型。 |
Indent | 获取或设置一个值,该值指示是否缩进元素。 |
IndentChars | 获取或设置缩进时要使用的字符串。 |
NamespaceHandling | 获取或设置一个值,该值指示在编写 XML 内容时, XmlWriter 是否应移除重复的命名空间声明。 的默认是输出程序中出现的所有命名空间声明。 |
NewLineChars | 获取或设置要用于分行符的字符串 |
NewLineHandling | 获取或设置一个值,该值指示是否将输出中的分行符正常化。 |
NewLineOnAttributes | 获取或设置一个值,该值指示是否将属性写入新行。 |
OmitXmlDeclaration | 获取或设置一个值指示省略 XML 声明。 |
Encoding | 获取或设置要使用的文本编码的类型。 |
Reset 方法 | 重置以上属性 |
API 官方地址: http://msdn.microsoft.com/zh-cn/library/system.xml.xmlwritersettings(v=vs.110).aspx
下面我们通过示例。来设置三个属性:去掉XML 声明,换行缩进,指定字符缩进,示例如下:
先定义一个方法
/// <summary> /// 设置三个属性:去掉XML声明,指定字符缩进 /// </summary> /// <param name="Obj"></param> /// <returns></returns> public static string ObjectToXmlSerializer(Object Obj) { System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings(); settings.OmitXmlDeclaration = true;//去除xml声明 settings.Encoding = Encoding.Default;//使用默认编码 settings.IndentChars = "--"; //使用指定字符缩进 settings.Indent = true; //换行缩进 System.IO.MemoryStream mem = new MemoryStream(); using (System.Xml.XmlWriter writer = System.Xml.XmlWriter.Create(mem, settings)) { //去除默认命名空间xmlns:xsd和xmlns:xsi XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("", ""); XmlSerializer formatter = new XmlSerializer(Obj.GetType()); formatter.Serialize(writer, Obj, ns); } return Encoding.Default.GetString(mem.ToArray()); }
调用如下:
//使用 设置 XmlWriterSettings 类的属性,来更改序列化的XML其他元素 //所在命名空间:System.Xml.XmlWriterSettings //设置三个属性:去掉XML声明,指定字符缩进 Console.WriteLine(ObjectToXmlSerializer(myArticleList));
输出结果和之前进行对比,如下图:
(1)了解 XML 的序列化,将对象转为 XML 数据格式
(2)了解 XML 的反序列化,将 XML 文本转换为对象
(3)了解 XML 序更化的注意事项 , 主要有以下几点:
(4)改变XML序列化的默认值:
使用 XmlSerializerNamespaces类(System.Xml.Serialization.XmlSerializerNamespaces),更改或删除XML的命名空间;
使用 XmlWriterSettings类(System.Xml.XmlWriterSettings)的属性,来更改 XML 的明名、缩进等元素。
下节我们将对通过实现序列化接口 IXmlSerializable 来介绍 XML 序列化以及介绍 XML 的其他特性,并举例说明 XML 序列化的高级应用。