深入理解:XML与对象的序列化与反序列化

  这篇文章主要讲述XML与对象的序列化与反序列化。并且会附上一些简单的序列化与反序列化方法,供大家使用。

  假设我们在一个Web项目中有这样两个类

  

复制代码 代码如下:

  public class Member

  {

  public string Num { get; set; }

  public string Name { get; set; }

  }

  public class Team

  {

  public  string Name;

  public  List<Member> Members { get; set; }

  }

  假设我们需要把Team类的一个实例POST到一个URL,

  当然,使用Form隐藏域提交就可以完成该功能。

  如果该Team包括30条数据呢?

  为了区分每个Member,我们得给参数的名字加上后缀。这就要一大串的隐藏域来完成:

  

复制代码 代码如下:

  <!--使用Razor来演示-->

  @model Team

  <form id="submitForm" action="http://www.johnconnor.com/team" method="post">

  <input type="hidden" name="TeamName" value="@Model.Name" />

  <input type="hidden" name="MemberNum1" value="@Model.Members[0].Num" />

  <input type="hidden" name="MemberName1" value="@Model.Members[0].Name" />

  ...

  <!--省略28X2个input标签-->

  <input type="hidden" name="MemberNum30" value="@Model.Members[29].Num" />

  <input type="hidden" name="MemberName30" value="@Model.Members[29].Name" />

  </form>

  <script type="text/javascript">

  document.getElementById("submitForm").submit();

  </script>

  还敢想象一下如果Team再复杂一些,嵌套再多一些的情况么?

  呃,即使你愿意这么传数据,对方看到一坨参数名就够头疼了。

  我们都知道对象是不能在网络中直接传输的,不过还有补救的办法。

  XML(Extensible Markup Language)可扩展标记语言,本身就被设计用来存储数据,任何一个对象都可以用XML来描述。以Team类为例:

  

复制代码 代码如下:

  <?xml version="1.0" encoding="utf-8"?>

  <Team xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

  <Name>Development</Name>

  <Members>

  <Member>

  <Num>001</Num>

  <Name>Marry</Name>

  </Member>

  <Member>

  <Num>002</Num>

  <Name>John</Name>

  </Member>

  </Members>

  </Team>

  这样一个XML文档就表示了Team一个实例。

  聪明的看官应该已经想到,XML是可以作为对象信息的载体在网络中传输,因为它是文本形式的。

  怎么进行XML文档与对象的相互转换呢?

  XmlSerializer类就是干这个活的。

        命名空间:System.Xml.Serialization

  程序集:System.Xml(在 system.xml.dll 中)

  现在这里展示了一个提供序列化与反序列化方法的EncodeHelper类。

  Deserialize方法将XML字符串转换为指定类型的对象;

  Serialize方法则将对象转换为XML字符串。

  

复制代码 代码如下:

  /// <summary>

  /// 提供xml文档序列化 反序列化

  /// </summary>

  public sealed class EncodeHelper

  {

  /// <summary>

  /// 反序列化XML字符串为指定类型

  /// </summary>

  public static object Deserialize(string Xml, Type ThisType)

  {

  XmlSerializer xmlSerializer = new XmlSerializer(ThisType);

  object result;

  try

  {

  using (StringReader stringReader = new StringReader(Xml))

  {

  result = xmlSerializer.Deserialize(stringReader);

  }

  }

  catch (Exception innerException)

  {

  bool flag = false;

  if (Xml != null)

  {

  if (Xml.StartsWith(Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble())))

  {

  flag = true;

  }

  }

  throw new ApplicationException(string.Format("Couldn't parse XML: '{0}'; Contains BOM: {1}; Type: {2}.",

  Xml, flag, ThisType.FullName), innerException);

  }

  return result;

  }

  /// <summary>

  /// 序列化object对象为XML字符串

  /// </summary>

  public static string Serialize(object ObjectToSerialize)

  {

  string result = null ;

  try

  {

  XmlSerializer xmlSerializer = new XmlSerializer(ObjectToSerialize.GetType());

  using (MemoryStream memoryStream = new MemoryStream())

  {

  XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, new UTF8Encoding(false));

  xmlTextWriter.Formatting = Formatting.Indented;

  xmlSerializer.Serialize(xmlTextWriter, ObjectToSerialize);

  xmlTextWriter.Flush();

  xmlTextWriter.Close();

  UTF8Encoding uTF8Encoding = new UTF8Encoding(false, true);

  result= uTF8Encoding.GetString(memoryStream.ToArray());

  }

  }

  catch (Exception innerException)

  {

  throw new ApplicationException("Couldn't Serialize Object:" + ObjectToSerialize.GetType().Name, innerException);

  }

  return result;

  }

  }

  要使用这个类需要添加以下引用

  using System;

  using System.Text;

  using System.IO;

  using System.Xml;

  using System.Xml.Serialization;

  下面我们用一个控制台程序来演示一下这个类是如何工作的。这里是程序的Main函数。

  

复制代码 代码如下:

  static void Main(string[] args)

  {

  List<Member> Members = new List<Member>();

  Member member1 = new Member { Name = "Marry", Num = "001" };

  Member member2 = new Member { Name = "John", Num = "002" };

  Members.Add(member1);

  Members.Add(member2);

  Team team = new Team { Name = "Development", Members = Members };

  var xml =EncodeHelper.Serialize(team);//序列化

  Console.Write(xml);//打印序列化后的XML字符串

  Console.ReadLine();

  Team newTeam = EncodeHelper.Deserialize(xml, typeof(Team)) as Team;//反序列化时需要显式的进行类型转换

  Console.WriteLine("Team Name:"+newTeam.Name);//显示反序列化后的newTeam对象

  foreach (var member in newTeam.Members)

  {

  Console.WriteLine("Member Num:" + member.Num);

  Console.WriteLine("Member Name:" + member.Name);

  }

  Console.ReadLine();

  }

  在执行完Console.Write(xml)这行代码后,就可以看到打印出来的XML文档了。

  

复制代码 代码如下:

  <?xml version="1.0" encoding="utf-8"?>

  <Team xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

  <Name>Development</Name>

  <Members>

  <Member>

  <Num>001</Num>

  <Name>Marry</Name>

  </Member>

  <Member>

  <Num>002</Num>

  <Name>John</Name>

  </Member>

  </Members>

  </Team>

  与我在文章开头给出的例子是一模一样的。

  最终反序列化出来的newTeam对象打印出来是这样的结果。

  Team Name:Development

  Member Num:001

  Member Name:Marry

  Member Num:002

  Member Name:John

  回到我们开头的Web通信的例子,

  利用XML序列化与反序列化来进行对象传递,我们只需要把需要传递的对象序列化为XML字符串,使用一个隐藏域进行form提交就可以搞定咯!

  接收方再将接收到的XML字符串反序列化成预设的对象即可。前提是双方必须约定序列化与反序列化的过程一致,且对象相同。

  最后我们来看一下怎么利用一些特性来控制序列化与反序列化操作的过程。我们把开始的类改一下:

  

复制代码 代码如下:

  public class Member

  {

  [XmlElement("Member_Num")]

  public string Num { get; set; }

  public string Name { get; set; }

  }

  [XmlRoot("Our_Team")]

  public class Team

  {

  [XmlIgnore]public string Name;

  public List<Member> Members { get; set; }

  }

  然后我们再次执行刚才的控制台程序,序列化结果变成了这样:

  

复制代码 代码如下:

  <?xml version="1.0" encoding="utf-8"?>

  <Our_Team xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

  <Members>

  <Member>

  <Member_Num>001</Member_Num>

  <Name>Marry</Name>

  </Member>

  <Member>

  <Member_Num>002</Member_Num>

  <Name>John</Name>

  </Member>

  </Members>

  </Our_Team>

  本来的根节点Team变成了Our_Team,Member的子节点Num变成了Member_Num,并且Team的Name子节点被忽略了。

  可见特性XmlRoot可以控制根节点的显示和操作过程,XmlElement则针对子节点。如果某些成员被标记XmlIgnore,则在序列化与反序列化过程中会被忽略。

  这些特性的具体内容可以在MSDN查看,就不多讲了。

  有了这些知识,在网络中传递对象数据应该已经难不倒各位看官了把。^_^