.NET中XML序列化的总结
【题外话】
以前虽然常用.NET中的序列化,但是常用的BinaryFormatter,也就是二进制文件的序列化,却鲜用XML的序列化。对于XML序列化,.NET中同样提供了一个非常方便的工具XmlSerializer,其可以很方便的将对象序列化为XML文件或将XML文件反序列化为对象。但是XML序列化与二进制序列化却又不少的区别,在刚开始的时候可能会遇到一些困惑。
【文章索引】
【一、XmlSerializer的作用】
.NET提供了非常方便的XML序列化工具XmlSerializer,与二进制序列化工具BinaryFormatter不同,XmlSerializer位于System.Xml.Serialization。根据MSDN上对XmlSerializer的说明来看,“XML 序列化是将对象的公共属性和字段转换为序列格式(这里是指 XML)以便存储或传输的过程。反序列化则是从 XML 输出中重新创建原始状态的对象。可以将序列化视为将对象的状态保存到流或缓冲区的方法”,也就是说,我们可以直接用XmlSerializer序列化对象中的属性和字段。
需要注意的是,只有public的属性和字段才是可以被序列化的,如果设置的为internal或者private的属性或字段都是不能被序列化的。当然,要序列化的对象的类也必须是public的,否则会抛出下列的异常:
除此之外,要想序列化对象中的字段或者属性,还需要保证字段和属性是可读可写的。例如,readonly的字段是不可以序列化的,没有get或set访问器的属性也是不可以序列化的(当然你可以选择在set访问器里什么也不写,那么虽然能序列化,但是反序列化的时候就成空的啦)。当然,static和const的字段和属性也是不会被序列化的,标记为[Obsolete]的也不会被序列化。此外,除了要求类是public的以外,还需要其有一个无参的构造方法,否则也会抛出异常。
关于XmlSerializer的使用,其实非常简单,只需要几行代码即可实现将一个对象序列化:


void SaveToFile(String filePath, Object obj)
{
FileStream fs = null; try
{
fs = new FileStream(filePath, FileMode.Create, FileAccess.Write);
XmlSerializer xs = new XmlSerializer(obj.GetType()); xs.Serialize(fs, obj);
}
finally
{
if (fs != null)
{
fs.Close();
}
}
}
或者,反序列化。


T LoadFromFile<T>(String filePath)
{
FileStream fs = null; try
{
fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
XmlSerializer xs = new XmlSerializer(typeof(T)); return (T)xs.Deserialize(fs);
}
finally
{
if (fs != null)
{
fs.Close();
}
}
}
【二、自定义XML结构的映射】
如果按上述的代码进行序列化,则可以将对象中的所有公共属性和字段都序列化进XML文件中。对象中的每个属性或字段都会序列化为一个子元素,如果对象中还有其他的对象或者数组等还会有更深的子元素。但是有时候我们可能除了子元素外还需要序列化节点的属性,或者需要修改映射的名称等等,那么我们就需要对类中的属性或者字段添加特性(Attributes)了。
与XML序列化相关的常见的特性有:
1、[XmlAttribute]:可以将指定字段或属性序列化为元素的属性,而不是子元素。除了直接在字段或属性上方直接写“[XmlAttribute]”外,还可以对其传入参数,例如“[XmlAttribute("identity")]”,可以改变映射的名称。例如:
[XmlAttribute("identity")]
public Int32 ID;
类定义及序列化后的结果如下:


public class Student
{
[XmlAttribute("identity")]
public Int32 ID;
public String Name;
}


<?xml version="1.0"?>
<Student xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" identity="1">
<Name>姓名</Name>
</Student>
2、[XmlElement]:虽然默认就可以将字段或属性序列化为子元素,但是如果要修改映射的名称,还是需要借助这个特性的。与[XmlAttribute]类似,其也可以不传入或传入参数,当不传入参数时,与不加该特性相同;当传入参数时,则可以修改映射的名称。例如:
[XmlElement("UserName")]
public String Name;
类定义及序列化后的结果如下:


public class Student
{
public Int32 ID;
[XmlElement("UserName")]
public String Name;
}


<?xml version="1.0"?>
<Student xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ID>1</ID>
<UserName>姓名</UserName>
</Student>
3、[XmlText]:除了能序列化为属性或者子元素外,还可以直接作为该元素的文本内容(InnerText),例如有个类Student,有一个ID我们希望序列化为属性,还有一个Name我们希望直接作为Student的内容而不是子元素,那么我们就可以在Name上使用[XmlText]了。例如:
[XmlText]
public String Name;
类定义及序列化后的结果如下:


public class Student
{
[XmlAttribute]
public Int32 ID;
[XmlText]
public String Name;
}


<?xml version="1.0"?>
<Student xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" ID="1">姓名</Student>
4、[XmlIgnore]:如果一个属性或字段我们不希望序列化(比如该属性是通过其他字段获取到的,并没有set访问器等等),那么我们可以通过[XmlIgnore]来让序列化器来忽略这个属性或字段。例如:
[XmlIgnore]
public Int32 NameLength { get { return this.Name.Length; } }
类定义及序列化后的结果如下:


public class Student
{
public Int32 ID;
public String Name;
[XmlIgnore]
public Int32 NameLength { get { return this.Name.Length; } }
}


<?xml version="1.0"?>
<Student xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ID>1</ID>
<Name>姓名</Name>
</Student>
5、[XmlArray]:如果需要序列化一个数组或者List等,但是需要修改映射的名称,那么我们就会用到[XmlArray]。需要注意的是,虽然数组等序列化出来的也是一个子元素,但是尽量不要用[XmlElement],否则数组里的每一个元素相当于对象的直接子元素(除非这个类本身序列化成子元素的就很少或没有,类似使用[XmlText]的情况),下边会给出对比。与[XmlElement]等相类似,如果不设置参数的话,那么与不添加特性相同;而对其设置参数后,则可以修改子元素的名称。例如:
[XmlArray("AllScore")]
public List<Int32> Scores;
类定义及序列化后的结果如下:


public class Student
{
public Int32 ID;
public String Name;
[XmlArray("AllScore")]
public List<Int32> Scores;
[XmlElement("FamilyMember")]
public List<String> FamilyNames;
}


<?xml version="1.0"?>
<Student xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ID>1</ID>
<Name>姓名</Name>
<AllScore>
<int>80</int>
<int>75</int>
<int>89</int>
</AllScore>
<FamilyMember>父亲姓名</FamilyMember>
<FamilyMember>母亲姓名</FamilyMember>
</Student>
6、[XmlArrayItem]:上述虽然对数组的名称进行了映射,但是数组里每一个元素的名称却没有定义,所以导致的结果是,所有数组里元素的名称都是按照类型名称来的,比如Int32类型的元素的元素名就是int等等,所以我们需要使用[XmlArrayItem]特性进行设置,增加上参数以后就可以映射数组里元素的名称了。例如:
[XmlArray("AllScore")]
[XmlArrayItem("Score")]
public List<Int32> Scores;
类定义及序列化后的结果如下:


public class Student
{
public Int32 ID;
public String Name;
[XmlArray("AllScore")]
[XmlArrayItem("Score")]
public List<Int32> Scores;
}


<?xml version="1.0"?>
<Student xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ID>1</ID>
<Name>姓名</Name>
<AllScore>
<Score>80</Score>
<Score>75</Score>
<Score>89</Score>
</AllScore>
</Student>
6、[XmlRoot]:对于类的名称如果要映射的话,就不能使用上述任何一个特性了,因为如果对类使用的话会提示“它只在“property, indexer, field, param, return”声明中有效”。这时候我们就需要[XmlRoot]这个特性,同样的,对其设置参数,即可以完成对类名称的映射。例如:
[XmlRoot("StudentInfo")]
public class Student { }
上述几个特性除了[XmlIgnore]以外,都还支持设置命名参数,如下图。
例如[XmlElement]、[XmlArray]等可以设置Order参数,就是可以强制设置子元素出现位置的先后顺序,例如:
public class Student
{
[XmlElement(Order = )]
public Int32 ID;
[XmlElement(Order = )]
public String Name;
}
【三、不能序列化的内容】
不像BinaryFormatter,XML序列化是有很多东西是不能序列化的,比如众所周知的Dictionary,我们其实可以通过.NET的源代码来查看到底哪些东西不能序列化为XML。通过序列化Dictionary抛出异常,可以找到如下这个类的相关方法,在.NET源代码的“Source\Net\3.5.50727.3053\DEVDIV\depot\DevDiv\releases\whidbey\netfxsp\ndp\fx\src\Xml\System\Xml\Serialization\Types.cs”目录下可以找到。
根据源代码,可以发现不能序列化的有以下的类型:
1、继承IDictionary接口的类型,这个众所周知了。.NET判断凡是实现了ICollection接口的都要去System.Xml.Serialization.TypeScope.GetDefaultIndexer()判断是否继承了IDictionary接口,如果继承了则抛出异常。
2、维度大于1的数组,在System.Xml.Serialization.TypeScope.ImportTypeDesc()里有判断维度是否大于1,如果维度大于1就抛出异常。
3、ValueType类型,别担心,这个不是指所有值类型的不能被序列化,源代码里判断的是“type == typeof(ValueType)”,所以特指ValueType类型的不能被序列化。p.s.我才知道竟然可以创建ValueType类型的变量。
此外,只要满足第一节里提到的XML序列化的要求的,都能被序列化,整理如下:
1、定义的类或者结构体或者枚举必须为public,类或结构体必须有无参的构造方法。比如System.Drawing.Font就无法实现序列化,因为其没有无参的构造方法。
2、要序列化的字段或属性必须为public,并且不能为static,标记为[Obsolete]的不会被序列化。字段不能为readonly或const,属性必须同时有set和get访问器。比如System.Drawing.Color序列化后不包含任何内容,因为其所有的公有属性全部只有get访问器,没有set访问器。
【四、输出格式的设置】
如果对序列化后的XML文件的输出格式有要求,比如要修改XML文件的编码、设置XML文件缩进、设置XML的命名空间等等,那么我们可以通过XmlWriter来实现我们的要求。XmlWriter可以通过XmlWriter.Create创建,可以写入到流、或者直接写入到文件路径或者写入到一个StringBuilder中。
设置XML文件的编码、缩进等可以通过创建XmlWriterSettings来设置,例如可以将缩进字符以及换行字符去除以达到减少文件大小的目的。
XmlWriterSettings settings = new XmlWriterSettings();
settings.Encoding = Encoding.ASCII;
settings.IndentChars = "";
settings.NewLineChars = "";
//或者也可以这样
//settings.Indent = false;
//settings.NewLineHandling = NewLineHandling.None; XmlWriter xw = XmlWriter.Create(fs, settings);
XmlSerializer xs = new XmlSerializer(obj.GetType());
xs.Serialize(xw, obj);
而对于设置XML命名空间,则可以创建XmlSerializerNamespaces,比如可以添加空的命名空间以取消默认设置的命名空间。
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add(String.Empty, String.Empty); //省略部分代码 xs.Serialize(xw, obj, namespaces);
【相关链接】
- XmlSerializer 类:http://msdn.microsoft.com/zh-cn/library/system.xml.serialization.xmlserializer.aspx
- 在.net中序列化读写xml方法的总结:http://www.cnblogs.com/fish-li/archive/2013/05/05/3061816.html
.NET中XML序列化的总结的更多相关文章
- .net的XML对象序列化VS WCF中xml序列化问题
整理一下 .net 对象序列化注意事项: 1. 字段:必须是 public类型 2.属性:只读或者只写的属性不被序列化,只有 可读可写并且赋值的才可以 序列化: Someclass obj = new ...
- .NET中XML序列化和反序列化常用类和用来控制XML序列化的属性总结(XmlSerializer,XmlTypeAttribute,XmlElementAttribute,XmlAttributeAttribute,XmlArrayAttribute...)
序列化和反序列化是指什么? 序列化(seriallization): 将对象转化为便于传输的数据格式, 常见的序列化格式:二进制格式,字节数组,json字符串,xml字符串.反序列化(deserial ...
- c# 中xml序列化时相同节点存入不同类型值
先上需要序列话的类定义: [System.Xml.Serialization.XmlIncludeAttribute(typeof(DescriptionType))] [System.CodeDom ...
- 关于XML序列化与CultureInfo
不同的计算机系统可能有着不同的CultureInfo,例如在中文环境下日期通常这样显示03/30/2016,而在有的操作系统下它可能是这样的30.3.2016. 这样的话带来一个问题,例如在中文环境下 ...
- 【ASP.NET Web API教程】6.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 W ...
- C#中XML与对象之间的序列化、反序列化
直接上代码: using System; using System.IO; using System.Text; using System.Xml; using System.Xml.Serializ ...
- <经验杂谈>C#/.Net中xml的Serialization序列化与DeSerializetion反序列化
1.先讲概念:.Net Framework提供了对应的System.Xml.Seriazliation.XmlSerializer负责把对象序列化到XML,和从XML中反序列化为对象.Serializ ...
- ASP.NET Web API中的JSON和XML序列化
ASP.NET Web API中的JSON和XML序列化 前言 阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok ...
随机推荐
- phpcms 整合 discuz!
第一步,进入discuz后台,点击UCenter菜单,然后点击应用管理,接着点击右侧的添加新应用按钮 然后填写应用相关信息: 1.应用类型选择“其他” 2.应该名称填写“phpcms” //此处可以自 ...
- Python-面向对象
面向过程变成:函数式变成,C程序等 面向对象编程:C++,Java,Python等 类和对象: 类:是对事物的抽象,比如人类.球类 对象:是类的一个实例,比如足球.篮球 实例说明: 球类可以对 ...
- ubuntu14.04 安装 搜狗输入法
1.安装或者更新fcitx libssh2-1:sudo apt-get install fcitx libssh2-1; 2.搜索是否安装成功: dpkg -l | grep fcitx ; dp ...
- xsl: normalize-space(string str) 函数
本文出自http://technet.microsoft.com/zh-cn/magazine/ms256063%28VS.90%29.aspx 通过去掉前导和尾随空白并使用单个空格替换一系列空白字符 ...
- 递推+高精度 UVA 10497 Sweet Child Makes Trouble(可爱的孩子惹麻烦)
题目链接 题意: n个物品全部乱序排列(都不在原来的位置)的方案数. 思路: dp[i]表示i个物品都乱序排序的方案数,所以状态转移方程.考虑i-1个物品乱序,放入第i个物品一定要和i-1个的其中一个 ...
- 单片机与控制实验(5)——重量测量并在LCD12864显示
一.实验目的和要求 掌握点阵式液晶显示屏的原理和控制方法,掌握点阵字符的显示方法.掌握模拟/数字(A/D)转换方式,进一步掌握使用C51语言编写程序的方法,使用C51语言编写实现重量测量的功能. 二. ...
- IE6图片元素img下高度超出出现多余空白
将图片转换为块级元素:display:block; 设置图片的垂直对齐方式:vertical-align属性为top,text-top,bottom,text-bottom 设置父元素的字体大小为0p ...
- ACM: Gym 101047M Removing coins in Kem Kadrãn - 暴力
Gym 101047M Removing coins in Kem Kadrãn Time Limit:2000MS Memory Limit:65536KB 64bit IO Fo ...
- Win 通过修改注册表把CapsLock映射为Rshift
成品: REGEDIT4 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout] "Scancod ...
- Python微信-- 分享接口(分享到朋友圈、朋友、空间)
生成JS-SDK权限验证的签名 获取signature(签名)首先要获得 1.#获得jsapi_ticket 2.#获取当前页面的url #获取当前页面的url url="{}://{}{} ...