.NET对象的XML序列化和反序列化
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
.NET对象的XML序列化和反序列化
序列化是指⼀个对象的实例可以被保存,保存成⼀个⼆进制串,当然,⼀旦被保存成⼆进制串,那么也可以保存成⽂本串了。
⽐如,⼀个计数器,数值为2,我们可以⽤字符串“2”表⽰。
如果有个对象,叫做connter,当前值为2,那么可以序列化成“2”,反向的,也可以从“2”得到值为2的计数器实例。
这样,关机时序列化它,开机时反序列化它,每次开机都是延续的。
不会都是从头开始。
序列化概念的提出和实现,可以使我们的应⽤程序的设置信息保存和读取更加⽅便。
序列化有很多好处,⽐如,在⼀台机器上产⽣⼀个实例,初始化完毕,然后可以序列化,通过⽹络传送到另⼀台机器,然后反序列化,得到对象实例,之后再执⾏某些业务逻辑,得到结果,再序列化,返回第⼀台机器,第⼀台机器得到对象实例,得到结果。
这个例⼦是⽬前⽐较先进的“智能代理”的原理。
当前⽐较热⽕的web services使⽤soap协议,soap协议也是以对象的可序列化为基础的。
⼀概述
.NET Framework为处理XML数据提供了许多不同的类库。
XmlDocument类能让你像处理⽂件⼀样处理xml数据,⽽XmlReader、XmlWriter和它们的派⽣类使你能够将xml数据作为数据流处理。
XmlSerializer则提供了另外的⽅法,它使你能够将⾃⼰的对象串⾏化和反串⾏化为xml。
串⾏化数据既能够让你像处理⽂件⼀样对数据进⾏随机处理,同时⼜能跳过你不感兴趣的数据。
⼆主要类库介绍
.NET ⽀持对象xml序列化和反序列化的类库主要位于命名空间System.Xml.Serialization中。
1. XmlSerializer 类
该类⽤⼀种⾼度松散耦合的⽅式提供串⾏化服务。
你的类不需要继承特别的基类,⽽且它们也不需要实现特别的接⼝。
相反,你只需在你的类或者这些类的公共域以及读/写属性⾥加上⾃定义的特性。
XmlSerializer通过反射机制读取这些特性并⽤它们将你的类和类成员映射到xml元素和属性。
2. XmlAttributeAttribute 类
指定类的公共域或读/写属性对应xml⽂件的Attribute。
例:[XmlAttribute(“type”)] or [XmlAttribute(AttributeName=”type”)]
3. XmlElementAttribute类
指定类的公共域或读/写属性对应xml⽂件的Element。
例:[XmlElement(“Maufacturer”)] or [XmlElement(ElementName=”Manufacturer”)]
4. XmlRootAttribute类
Xml序列化时,由该特性指定的元素将被序列化成xml的根元素。
例:[XmlRoot(“RootElement”)] or [XmlRoot(ElementName = “RootElements”)]
5. XmlTextAttribute 类
Xml序列化时,由该特性指定的元素值将被序列化成xml元素的值。
⼀个类只允许拥有⼀个该特性类的实例,因为xml元素只能有⼀个值。
6. XmlIgnoreAttribute类
Xml序列化时不会序列化该特性指定的元素。
三实例
下⾯例⼦中的xml schema 描述了⼀个简单的⼈⼒资源信息,其中包含了xml的⼤部分格式,如xml 元素相互嵌套, xml元素既有元素值,⼜有属性值。
1. 待序列化的类层次结构
[XmlRoot("humanResource")]
public class HumanResource
{
#region private data.
private int m_record = 0;
private Worker[] m_workers = null;
#endregion
[XmlAttribute(AttributeName="record")]
public int Record
{
get { return m_record; }
set { m_record = value; }
}
[XmlElement(ElementName="worker")]
public Worker[] Workers
{
get { return m_workers; }
set { m_workers = value; }
}
}
public class Worker
{
#region private data.
private string m_number = null;
private InformationItem[] m_infoItems = null;
#endregion
[XmlAttribute("number")]
public string Number
{
get { return m_number; }
set { m_number = value; }
}
[XmlElement("infoItem")]
public InformationItem[] InfoItems
{
get { return m_infoItems; }
set { m_infoItems = value; }
}
}
public class InformationItem
{
#region private data.
private string m_name = null;
private string m_value = null;
#endregion
[XmlAttribute(AttributeName = "name")]
public string Name
{
get { return m_name; }
set { m_name = value; }
}
[XmlText]
public string Value
{
get { return m_value; }
set { m_value = value; }
}
}
2. 序列化⽣成的xml结构
<?xml version="1.0" ?>
<humanResource xmlns:xsi="/2001/XMLSchema-instance" xmlns:xsd="/2001/XMLSchema" record="2">
<worker number="001">
<infoItem name="name">Michale</infoItem>
<infoItem name="sex">male</infoItem>
<infoItem name="age">25</infoItem>
</worker>
<worker number="002">
<infoItem name="name">Surce</infoItem>
<infoItem name="sex">male</infoItem>
<infoItem name="age">28</infoItem>
</worker>
</humanResource>
3. 利⽤XmlSerializer类进⾏序列化和反序列化的实现(⼀般利⽤缓存机制实现xml⽂件只解析⼀次。
)
public sealed class ConfigurationManager
{
private static HumanResource m_humanResource = null;
private ConfigurationManager(){}
public static HumanResource Get(string path)
{
if (m_humanResource == null)
{
FileStream fs = null;
try
{
XmlSerializer xs = new XmlSerializer(typeof(HumanResource));
fs = new FileStream(path, FileMode.Open, FileAccess.Read);
m_humanResource = (HumanResource)xs.Deserialize(fs);
fs.Close();
return m_humanResource;
}
catch
{
if (fs != null)
fs.Close();
throw new Exception("Xml deserialization failed!");
}
}
else
{
return m_humanResource;
}
}
public static void Set(string path, HumanResource humanResource)
{
if (humanResource == null)
throw new Exception("Parameter humanResource is null!");
FileStream fs = null;
try
{
XmlSerializer xs = new XmlSerializer(typeof(HumanResource));
fs = new FileStream(path, FileMode.Create, FileAccess.Write);
xs.Serialize(fs, humanResource);
m_humanResource = null;
fs.Close();
}
catch
{
if (fs != null)
fs.Close();
throw new Exception("Xml serialization failed!");
}
}
}
四说明
1. 需要序列化为xml元素的属性必须为读/写属性;
2. 注意为类成员设定缺省值,尽管这并不是必须的。
“序列化”可被定义为将对象的状态存储到存储媒介中的过程。
在此过程中,对象的公共字段和私有字段以及类的名称(包括包含该类的程序集)都被转换为字节流,然后写⼊数据流。
在以后“反序列化”该对象时,创建原始对象的精确复本。
⼀、为什么要选择序列化
⼀个原因是将对象的状态保持在存储媒体中,以便可以在以后重新创建精确的副本;
另⼀个原因是通过值将对象从⼀个应⽤程序域发送到另⼀个应⽤程序域中。
例如,序列化可⽤于在 中保存会话状态并将对象复制到 Windows 窗体的剪贴板中。
远程处理还可以使⽤序列化通过值将对象从⼀个应⽤程序域传递到另⼀个应⽤程序域中。
⼆、如何实现对象的序列化及反序列化
要实现对象的序列化,⾸先要保证该对象可以序列化。
⽽且,序列化只是将对象的属性进⾏有效的保存,对于对象的⼀些⽅法则⽆法实现序列化的。
实现⼀个类可序列化的最简便的⽅法就是增加Serializable属性标记类。
如:
[Serializable()]
public class MEABlock
{
private int m_ID;
public string Caption;
public MEABlock()
{
///构造函数
}
}
即可实现该类的可序列化。
要将该类的实例序列化为到⽂件中?.NET FrameWork提供了两种⽅法:
1、XML序列化
使⽤ XmLSerializer 类,可将下列项序列化。
公共类的公共读/写属性和字段
实现 ICollection 或 IEnumerable 的类。
(注意只有集合会被序列化,⽽公共属性却不会。
)
XmlElement 对象。
XmlNode 对象。
DataSet 对象。
要实现上述类的实例的序列化,可参照如下例⼦:
MEABlock myBlock = new MEABlock();
// Insert code to set properties and fields of the object.
XmlSerializer mySerializer = new XmlSerializer(typeof(MEABlock));
// To write to a file, create a StreamWriter object.
StreamWriter myWriter = new StreamWriter("myFileName.xml");
mySerializer.Serialize(myWriter, MEABlock);
需要注意的是XML序列化只会将public的字段保存,对于私有字段不予于保存。
⽣成的XML⽂件格式如下:
<MEABlock>
<Caption>Test</Caption>
</MEABlock>
对于对象的反序列化,则如下:
MEABlock myBlock;
// Constructs an instance of the XmlSerializer with the type
// of object that is being deserialized.
XmlSerializer mySerializer = new XmlSerializer(typeof(MEABlock));
// To read the file, creates a FileStream.
FileStream myFileStream = new FileStream("myFileName.xml", FileMode.Open);
// Calls the Deserialize method and casts to the object type.
myBlock = (MEABlock)mySerializer.Deserialize(myFileStream)
2、⼆进制序列化
与XML序列化不同的是,⼆进制序列化可以将类的实例中所有字段(包括私有和公有)都进⾏序列化操作。
这就更⽅便、更准确的还原了对象的副本。
要实现上述类的实例的序列化,可参照如下例⼦:
MEABlock myBlock = new MEABlock();
// Insert code to set properties and fields of the object.
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream("MyFile.bin",FileMode.Create,FileAccess.Write, FileShare.None);
formatter.Serialize(stream, myBlock);
stream.Close();
对于对象的反序列化,则如下:
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream("MyFile.bin", FileMode.Open,FileAccess.Read, FileShare.Read);
MEABlock myBlock = (MEABlock) formatter.Deserialize(stream);
stream.Close();
三、如何变相实现⾃定义可视化控件的序列化、反序列化
对于WinForm中⾃定义控件,由于继承于System.Windows.Form类,⽽Form类⼜是从MarshalByRefObject继承的,窗体本⾝⽆法做到序列化,窗体的实现基于Win32下GUI 资源,不能脱离当前上下⽂存在。
当然可以采⽤变通的⽅法实现控件的序列化。
这⾥采⽤的是记忆类模型。
定义记忆类(其实就是⼀个可序列化的实体类)⽤于记录控件的有效属性,需要序列化控件的时候,只需要将该控件的实例Copy到记忆类,演变成序列化保存该记忆类的操作。
反序列化是⼀个逆过程。
将数据流反序列化成为该记忆类,再根据该记忆类的属性⽣成控件实例。
⽽对于控件的⼀些事件、⽅法则可以继续使⽤。
.NET Framework 开发员指南
序列化是将对象转换为容易传输的格式的过程。
例如,可以序列化⼀个对象,然后使⽤ HTTP 通过 Internet 在客户端和服务器之间传输该对象。
在另⼀端,反序列化将从该流重新构造对象。
XML 序列化仅将对象的公共字段和属性值序列化为 XML 流。
XML 序列化不包括类型信息。
例如,如果您有⼀个存在于 Library 命名空间中的 Book 对象,将不能保证它将会被反序列化为同⼀类型的对象。
注意 XML 序列化不转换⽅法、索引器、私有字段或只读属性(只读集合除外)。
若要序列化对象的所有字段和属性(公共的和私有的),请使⽤,⽽不要使⽤XML 序列化。
XML 序列化中最主要的类是类,它的最重要的⽅法是 Serialize 和 Deserialize ⽅法。
XmlSerializer ⽣成的 XML 流符合万维⽹联合会 () XML 架构定义语⾔ (XSD) 1.0 的建议。
另外,⽣成的数据类型符合标题为“XML Schema Part 2: Datatypes”(XML 架构第⼆部分:数据类型)的⽂档。
对象中的数据是⽤编程语⾔构造(如类、字段、属性、基元类型、数组,甚⾄ XmlElement 或 XmlAttribute 对象形式的嵌⼊ XML)来描述的。
您可以创建⾃⼰的⽤属性批注的类,或者使⽤ XML 架构定义⼯具⽣成基于现有 XML 架构的类。
如果您有 XML 架构,就可运⾏ XML 架构定义⼯具⽣成⼀组强类型化为架构并⽤属性批注的类。
当序列化这样的类的实例时,⽣成的 XML 符合 XML 架构。
使⽤这样的类,就可针对容易操作的对象模型进⾏编程,同时确保⽣成的 XML 符合 XML 架构。
这是使⽤ .NET Framework 中的其他类(如 XmlReader 和 XmlWriter 类)分析和写 XML 流的⼀种替换⽅法。
(有关使⽤这些类的更多信息,请参见。
)这些类使您可以分析任何 XML 流。
与此相反,当需要 XML 流符合已知的 XML 架构时,请使⽤ XmlSerializer。
属性控制由 XmlSerializer 类⽣成的 XML 流,使您可以设置 XML 流的 XML 命名空间、元素名、属性名等。
有关这些属性和它们如何控制 XML 序列化的更多信息,请参见。
若想获得⼀个列⽰对所⽣成的 XML 起控制作⽤的那些属性的表,请参见。
XmlSerializer 类可进⼀步序列化对象并⽣成编码的 SOAP XML 流。
⽣成的 XML 符合标题为“Simple Object Access Protocol (SOAP) 1.1”的万维⽹联合会⽂档的第 5 节。
有关此过程的更多信息,请参见。
有关控制⽣成的 XML 的属性表,请参见。
XmlSerializer 类⽣成由 XML Web services 创建和传递给 XML Web services 的 SOAP 消息。
若要控制 SOAP 消息,可将属性应⽤于 XML Web services ⽂件 (.asmx) 中的类、返回值、参数和字段。
您可以同时使⽤在“控制 XML 序列化的属性”中列出的属性和在“控制编码的 SOAP 序列化的属性”中列出的属性,因为 XML Web services 可以使⽤⽂本样式,也可以使⽤编码的 SOAP 样式。
有关使⽤属性控制 XML Web services 所⽣成的 XML 的更多信息,请参见。
有关 SOAP 和 XML Web services 的更多信息,请参见。
保护 XmlSerializer 应⽤程序
在创建使⽤ XmlSerializer 的应⽤程序时,您应当了解以下⼏点以及它们的影响:
XmlSerializer 创建 C# ⽂件 (.cs ⽂件),并将其编译成 .dll ⽂件,这些 .dll ⽂件位于由 TEMP 环境变量指定的⽬录中;这些 DLL ⽂件将发⽣序列化。
代码和 DLL 在创建和进⾏编译时,易于遭受恶意进程的攻击。
如果所使⽤的计算机运⾏的是 Microsoft Windows NT 4.0 或更⾼版本,则有可能会有两个或更多⽤户共享临时⽬录。
如果同时存在以下两种情况,则共享临时⽬录是有危险性的:(1) 两个帐户有不同的安全特权;(2) 具有较⾼特权的帐户运⾏⼀个使⽤ XmlSerializer 的应⽤程序。
在这种情况下,某⼀⽤户可以替换所编译的 .cs 或 .dll ⽂件,由此破坏计算机的安全性。
为了避免发⽣这⼀问题,请始终确保计算机上的每⼀帐户都有⾃⼰的配置⽂件。
如果能够保证这⼀点的话,默认情况下,TEMP 环境变量就会为不同的帐户指定不同的⽬录。
如果⼀名恶意⽤户向 Web 服务器发送持续的 XML 数据流(拒绝服务攻击),XmlSerializer 会⼀直处理这⼀数据,直到计算机资源不够⽤才停⽌。
如果您所使⽤的计算机运⾏ Internet 信息服务 (IIS),并且您的应⽤程序是在 IIS 下运⾏,就可以避免这类攻击。
IIS 带有⼀个控制门,⽤于禁⽌处理⼤于设定数量(默认值是
4 KB)的数据流。
如果您所创建的应⽤程序不使⽤ IIS,同时该应⽤程序使⽤ XmlSerializer 进⾏反序列化,则应该实现⼀个类似的控制门,以阻⽌拒绝服务攻击。
XmlSerializer 将使⽤给予它的任何类型,对数据进⾏序列化,并运⾏任何代码。
恶意对象施加威胁的⽅式有两种。
⼀种是运⾏恶意代码,另⼀种是将恶意代码插⼊到由 XmlSerializer 创建的 C# ⽂件中。
在第⼀种情况下,如果恶意对象试图运⾏破坏性过程,代码访问安全性将帮助防⽌发⽣任何破坏。
在第⼆种情况下,在理论上,恶意对象有可能会以某种⽅式将代码插⼊到由 XmlSerializer 创建的 C# ⽂件中。
尽管对这⼀问题已进⾏了彻底的检验,⽽且这类攻击被认为是不可能的,但您还是应该⼩⼼⼀些,⼀定不要序列化那些不可信的未知类型的数据。
已序列化的重要数据可能易于遭到攻击。
XmlSerializer 对数据进⾏了序列化之后,数据可以被存储为 XML ⽂件,或存储在其他数据存储区。
如果其他进程可以访问到您的数据存储区,或是可以在 Intranet 或Internet 上看到该数据存储区,数据就可能被窃取,并被恶意使⽤。
例如,如果您创建了⼀个应⽤程序,对包含信⽤卡号码的订单进⾏序列化,这⼀数据就⾮常重要。
为了防⽌发⽣这⼀问题,请始终保护您的数据存储区,并对数据采取保密措施。
简单类的序列化
下⾯的⽰例显⽰⼀个具有公共字段的简单类:
[Visual Basic]Public Class OrderForm Public OrderDate As DateTimeEnd Class[C#]public class OrderForm{ public DateTime OrderDate;}
当将此类的实例序列化时,该实例可能类似于下⾯的代码:
<OrderForm> <OrderDate>12/12/01</OrderDate></OrderForm>
有关序列化的更多⽰例,请参见。
可以序列化的项
使⽤ XmLSerializer 类,可将下列项序列化。
公共类的公共读/写属性和字段
实现 ICollection 或 IEnumerable 的类。
(注意只有集合会被序列化,⽽公共属性却不会。
)
XmlElement 对象。
XmlNode 对象。
DataSet 对象。
序列化和反序列化对象
若要序列化对象,⾸先创建要序列化的对象并设置它的公共属性和字段。
为此,必须确定要⽤以存储 XML 流的传输格式(或者作为流,或者作为⽂件)。
例如,如果 XML 流必
须以永久形式保存,则创建对象。
当您反序列化对象时,传输格式确定您将创建流还是⽂件对象。
确定了传输格式之后,就可以根据需要调⽤ Serialize 或 Deserialize ⽅法。
序列化对象
1. 创建对象并设置它的公共字段和属性。
2. 使⽤该对象的类型构造 XmlSerializer。
有关更多信息,请参见 XmlSerializer 类构造函数。
3. 调⽤ Serialize ⽅法以⽣成对象的公共属性和字段的 XML 流表⽰形式或⽂件表⽰形式。
下⾯的⽰例创建⼀个⽂件。
[Visual Basic]Dim myObject As MySerializableClass = New MySerializableClass()' Insert code to set properties and fields of the object.Dim mySerializer As XmlSerializer = New XmlSerializer(GetType(MySerializableClass))' To write to a 反序列化对象
1. 使⽤要反序列化的对象的类型构造 XmlSerializer。
2. 调⽤ Deserialize ⽅法以产⽣该对象的副本。
在反序列化时,必须将返回的对象强制转换为原始对象的类型,如下⾯的⽰例中所⽰。
下⾯的⽰例将该对象反序列化为⽂件,
尽管它也可以被反序列化为流。
[Visual Basic]Dim myObject As MySerializableClass' Constructs an instance of the XmlSerializer with the type' of object that is being deserialized.Dim mySerializer As XmlSerializer = New XmlSerializer(GetType(MySerializableClass))' To 有关 XML 序列化的更多⽰例,请参见。
使⽤ XML 序列化的好处
XmlSerializer 类在您将对象序列化为 XML 时为您提供完整⽽灵活的控制。
如果您正在创建 XML Web services,则可以将控制序列化的属性应⽤于类和成员以确保 XML 输出符
合特定的架构。
例如,XmlSerializer 使您能够:
指定应将字段或属性编码为特性还是元素。
指定要使⽤的 XML 命名空间。
如果字段或属性名不合适,则指定元素或特性的名称。
XML 序列化的另⼀个好处是:只要⽣成的 XML 流符合给定的架构,则对于所开发的应⽤程序就没有约束。
假定有这样⼀个⽤于描述图书的架构,它具有标题、作者、出版商和
ISBN 编号元素。
您可以开发⼀个以您希望的任何⽅式(例如,作为图书订单,或作为图书清单)处理 XML 数据的应⽤程序。
在任⼀种情况下,唯⼀的要求是 XML 流应当符合
指定的 XML 架构定义语⾔ (XSD) 架构。
XML 序列化注意事项
使⽤ XmlSerializer 类时,应考虑下列情况:
序列化数据只包含数据本⾝以及类的结构。
不包括类型标识和程序集信息。
只能序列化公共属性和字段。
如果需要序列化⾮公共数据,请使⽤ BinaryFormatter 类⽽不是 XML 序列化。
类必须有⼀个将由 XmlSerializer 序列化的默认构造函数。
不能序列化⽅法。
XmlSerializer 可以以不同⽅式处理实现 IEnumerable 或 ICollection 的类(条件是这些类满⾜某些要求)。
实现 IEnumerable 的类必须实现带单个参数的公共 Add ⽅法。
Add ⽅法的参数必须与从 GetEnumerator ⽅法返回的 IEnumerator.Current 属性所返回的类型⼀致(多态)。
除实现 IEnumerable 外还实现 ICollection 的类(如
CollectionBase)必须有⼀个取整数的公共 Item 索引属性(在 C# 中为索引器),并且它必须有⼀个整数类型的公共 Count 属性。
传递给 Add ⽅法的参数必须与从 Item 属
性返回的类型相同或与该类型的某个基的类型相同。
对于实现 ICollection 的类,要序列化的值将从索引 Item 属性检索,⽽不是通过调⽤ GetEnumerator 来检索。
另外请注
意,除返回另⼀个集合类(实现 ICollection 的集合类)的公共字段之外,将不序列化公共字段和属性。
有关⽰例,请参见。
XSD 数据类型映射
标题为“XML Schema Part 2: Datatypes”的万维⽹联合会 () ⽂档指定在 XML 架构定义语⾔ (XSD) 架构中允许使⽤的简单数据类型。
对于这些数据类型中的许多类型
(例如,int 和 decimal),在 .NET Framework 中都有对应的数据类型。
但是,某些 XML 数据类型在 .NET Framework 中没有对应的数据类型(例如,NMTOKEN 数据类
型)。
在这样的情况下,如果使⽤ XML 架构定义⼯具 () 从架构⽣成类,就会将适当的特性应⽤于字符串类型的成员,并会将其 DataType 属性设置为 XML 数据类型名称。
例
如,如果架构包含⼀个数据类型为 XML 数据类型 NMTOKEN 的、名为“MyToken”的元素,则⽣成的类可能包含以下⽰例中的成员。
[Visual Basic]<XmlElement(DataType:="NMTOKEN")>Public MyToken As String[C#][XmlElement(DataType = "NMTOKEN")]public string MyToken;
与此类似,如果创建⼀个必须符合特定 XML 架构 (XSD) 的类,应当应⽤适当的特性并将它的 DataType 属性设置为所需的 XML 数据类型名称。