[.net 面向对象程序设计进阶] (10) 序列化(Serialization)(二) 通过序列化博客园文章学习XML的序列化

[.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使用

1. 关于XML

  XML 指可扩展标记语言(EXtensible Markup Language)

  XML 是一种标记语言,很类似 HTML

  XML 的设计宗旨是传输数据,而非显示数据

  XML 标签没有被预定义。您需要自行定义标签。

  XML 被设计为具有自我描述性。

  XML 是 W3C 的推荐标准

   关于XML相关的基础知识,这里不作为重点赘述,下面入正题。

2. XML序列化

XML序列化的命名空间是:System.Xml.Serialization

类:XmlSerializer

主要方法:Serialize和Deserialize

下面先来一个简单有意思的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>

3. XML反序列化

Xml反序列化,即将Xml文本转化为对象的过程。

我们将上面序列化的文本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序列化和反序列化的基本知识,下面介绍更进一步介绍在序列化过程中的一些细节。

4. XML序列化一些注意事项

(1)要序列化的类必须有默认的构造的构造函数,才能使用XmlSerializer序列化,需要序列化的类都必须有一个无参的构造函数(通过对基础中类和类的实例学习,我们必须知道类不定义构造函数的情况下,会默认生成一个无参数的构造函数);

(2)索引器、私有字段或只读属性(只读集合属性除外)不能被序列化;

(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文档读取再执行一次。

以上十点注意,其中前三点,也就是加黑的这几点,是重点要知道的。

5. 改变XML序列化的默认值

5.1 更改或删除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("","");

当加入一个空的命名空间时,就可以删除默认的XML命名空间。

5.2  更改XML序列化的其他元素

我们先列出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));

输出结果和之前进行对比,如下图:

 

6. 本节要点:

(1)了解XML的序列化,将对象转为XML数据格式

(2)了解XML的反序列化,将XML文本转换为对象

(3)了解XML序更化的注意事项,主要有以下几点:

  • 默认类和无参数构造函数类才可以序列化;
  • 索引器、私有字段或只读属性(只读集合属性除外)不能被序列化
  • 有一些序列化的标记当不想序列化一个属性时,使用[System.Xml.Serialization.XmlIgnore]标记,能用于属性;
  • [NonSerializable]应用于属性无效,能用于类,结构体等。

(4)改变XML序列化的默认值:

使用 XmlSerializerNamespaces类(System.Xml.Serialization.XmlSerializerNamespaces),更改或删除XML的命名空间;

使用 XmlWriterSettings类(System.Xml.XmlWriterSettings)的属性,来更改XML的明名、缩进等元素。 

     下节我们将对通过实现序列化接口IXmlSerializable来介绍XML序列化以及介绍XML基本操作,并举例说明,最后会整理一个XML操作的通用类

==============================================================================================  

 返回目录

 <如果对你有帮助,记得点一下推荐哦,如有有不明白或错误之处,请多交流>  

<对本系列文章阅读有困难的朋友,请先看《.net 面向对象编程基础》>

<转载声明:技术需要共享精神,欢迎转载本博客中的文章,但请注明版权及URL>

.NET 技术交流群:467189533    .NET 程序设计

==============================================================================================   

posted @ 2015-07-09 09:38  yubinfeng  阅读(2122)  评论(4编辑  收藏  举报