65.9K
CodeProject 正在变化。 阅读更多。
Home

XML 序列化与反序列化:第二部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (76投票s)

2012年11月6日

CPOL

6分钟阅读

viewsIcon

295492

XML 的序列化与反序列化

引言

我之前的文章 XML 序列化与反序列化(第一部分) 讨论了将对象序列化为 XML 格式。在这篇文章中,我们将讨论如何将 XML "反序列化" 回对象形式。反序列化用于将数据字节(如 XML 或二进制数据)转换为 "Object" 类型。XML 文件可以使用反序列化重新转换回对象。

让我们从一个基本示例开始。这是需要反序列化的 XML 文件:

<AddressDetails>
  <HouseNo>4</HouseNo>
  <StreetName>Rohini</StreetName>
  <City>Delhi</City>
</AddressDetails>

因此,为了反序列化这个 XML 文件,我们需要创建一个类:

public class Address
{
    public int HouseNo { get; set; }
    public string StreetName { get; set; }
    public string City { get; set; }
}

这个类包含一个名为 `name` 的变量,它与 XML 标签相同,XML 标签的值默认会映射到类中的相应变量。类 "Address" 中的 "HouseNo" 将自动映射到 XML 标签 "HouseNo"。

现在,让我们看一个将此 XML 映射到类对象的简单程序:

public static void Main(string[] args)
{
    XmlSerializer deserializer = new XmlSerializer(typeof(Address));
    TextReader reader = new StreamReader(@"D:\myXml.xml");
    object obj = deserializer.Deserialize(reader);
    Address XmlData = (Address)obj;
    reader.Close();
}

"deserializer.Deserialize" 函数用于反序列化 XML 文件中的 XML 数据。现在,由于我们将 XML 文件结构反序列化为对象形式,我们可以访问 XML 标签值了:

Address.HouseNo
Address.StreetName
Address.City

在创建用于反序列化的类时,应注意以下几点:

  1. 类变量/属性应始终声明为 public
  2. 为了反序列化,我们需要有一个默认/无参数构造函数。

任何没有 "默认/无参数" 构造函数的类都会导致错误,因为 "deserializer.Deserialize(reader)" 没有提供向带参数构造函数传递值的方式。

在上面的代码中,我们有简单的 XML 元素,没有子元素。让我们进一步探索并处理一些更复杂的情况,即 XML 元素可能包含进一步的子标签:

让我们进一步复杂化情况,并尝试 "反序列化" 以下 "XML":

<?xml version="1.0"?>
<AddressDirectory>
  <Address>
    <HouseNo>1</HouseNo>
    <StreetName>Pitampura</StreetName>
    <City>Delhi</City>
  </Address>
  <Address>
    <HouseNo>4</HouseNo>
    <StreetName>Rohini</StreetName>
    <City>Delhi</City>
  </Address>
</AddressDirectory>

让我们看看这里的区别。在下面的 XML 中,我们有多个 "Address" 标签。并且 Address 标签还包含进一步的子标签。因此,我们需要创建我们的类,以便它可以包含多个 "Address" 标签及其子标签。让我们看看如何创建类:

public class AddressDirectory
{
    [XmlElement("Address")] 
    public List<Address> addressList = new List<Address>(); 
}
public class Address
{
    public int HouseNo { get; set; }
    public string StreetName { get; set; }
    public string City { get; set; }
}

请记住,在为任何标签的反序列化创建类时,我们只能深入一层。简单来说,举个例子。在上面的 XML 中,我们有 "AddressDirectory" 标签。为了反序列化,我们为 "AddressDirectory" 标签创建一个类。现在,这个类可以包含 "AddressDirectory" XML 标签的以下详细信息

  1. "AddressDirectory" 标签的属性(如果存在)。
  2. 它的子节点,例如 "Address"(只能访问 "Address" 标签,不能深入到 "Address" 标签的子节点)。
  3. InnerText(如果存在)。

AddressDirectory 无法提取关于 "Address" 的子标签以及 "Address" 标签的属性的信息。为了获取关于 "ChildNodes" (子节点) 关于 "Address" 标签的信息,我们需要创建另一个类来存储属性信息和childnode信息(最多第一层)。

在这里,我们创建了一个类 AddressDirectory,它映射到 "XML" 的根标签元素。根标签进一步包含 "Address" 标签。我们可以在此处有多个 "Address" 标签,因此我们在 "AddressDirectory" 类中创建了一个类 "Address" 的列表,以便可以存储多个 "Address" 标签的信息。这里,在类中,我们看到 XmlElement 写在 addressList 上。此属性用于类变量的名称与 XML 文件中的名称不同,因此为了将类变量与 XML 标签映射,我们使用关键字 XmlElement。我们将在文章后面讨论这一点。

我们将在文章后面看到更多关于此的示例。

反序列化 XML 需要执行的程序是:

XmlSerializer deserializer = new XmlSerializer(typeof(AddressDirectory));
TextReader reader = new StreamReader(@"D:\myXml.xml");
object obj = deserializer.Deserialize(reader);
AddressDirectory XmlData = (AddressDirectory)obj;
reader.Close();

结果对象 "XmlData" 将包含一个 "Address" 类型的对象列表。我们可以按如下方式访问第一个 Address 标签的数据:

XmlData.addressList[0].HouseNo;
XmlData.addressList[0].StreetName; 
XmlData.addressList[0].City;

XML 可以进一步复杂化,让我们看看下面的 XML 文件结构及其类表示:

<?xml version="1.0"?>
<AddressDirectory>
  <Owner>Mayank</Owner>
  <Age>24</Age>
  <Company>BIPL</Company>
  <Address>
    <HouseNo>1</HouseNo>
    <StreetName>Pitampura</StreetName>
    <City>Delhi</City>
  </Address>
  <Address>
    <HouseNo>4</HouseNo>
    <StreetName>Rohini</StreetName>
    <City>Delhi</City>
  </Address>
</AddressDirectory>

在这里,我们可以看到 "AddressDirectory" 中除了 "Address" 标签列表之外,还有一些额外的标签,如 "Owner"、"Age"、"Company"。所以 XML 的类结构将是:

public class AddressDirectory
{
    public string Owner { get; set; }
    public string Age { get; set; }
    public string Company { get; set; }
    [XmlElement("Address")]
    public List<Address> addressList = new List<Address>(); 
}

public class Address
{
    public string HouseNo { get; set; }
    public string StreetName { get; set; }
    public string City { get; set; }
}

"AddressDirectory" 标签的子节点存在于 AddressDirectory 类中,而 Address 标签的childnodes 存在于 address 类中。

注意:这里重要的是要观察,类只能包含那些是其直接子节点的“标签值”,即 AddressDirectory 只能包含其直接子节点的信息,如 "Owner"、"Company"、"Age" 和 "Address"。但是,这里 "Address" 进一步包含更多标签。 "Address" 标签的子节点不能由类 "AddresssDirectory" 表示。因此,我们需要为 "Address" 标签创建另一个类,该类存储 "Address" 类的childnode信息。 "Address" 类将进一步包含其直接childnode "HouseNo"、"StreetName"、"City" 的值。由于我们有多个 Address 标签,因此我们有一个 "List" 的 "Address" 类。

反序列化期间的 XML 属性

反序列化期间可能很有用的属性包括:

  1. XmlElement
  2. XmlAttribute
  3. XmlText

这三个属性提供映射信息。它提供了关于 XML 标签的哪个元素将映射到类的哪个变量的信息。

观察以下 XML:

<?xml version="1.0" encoding="utf-8" ?>
<AddressDirectory id="1">
  <DirectoryOwner>Mayank</DirectoryOwner>
  <PinCode>110085</PinCode>
  <Designation place="Delhi">Engineer</Designation>
  <Address AddressId="12">
    <HouseNo>4</HouseNo>
    <StreetName>Rohini</StreetName>
    <City>Delhi</City>  
  </Address>
  <Address AddressId="13">
    <HouseNo>4</HouseNo>
    <StreetName>Rohini</StreetName>
    <City>Delhi</City>
  </Address>
</AddressDirectory>

让我们观察此 XML 文件的不同组成部分:

  1. AddressDirectory 是 XML 文件的根节点
  2. AddressDirectory 包含一个 "XmlAttribute",名为 "id",值为 "1"。
  3. "AddressDirectory" 包含 "XmlElement",如 DirectoryOwnerAddressDesignationAddress
  4. "Designation" 标签包含一个 "XmlAttribute"("place")和一个 "XmlText"("Delhi")。

因此,从上面的 XML 中,我们可以找出什么是 "XmlElement"、"XmlAttribute"、"XmlText"。在反序列化这种复杂的 XML,其中可能包含所有这三个组件时,我们需要明确指定类变量存储的是 "Element"、"Attribute" 还是 "XmlText"。

让我们尝试反序列化此 XML:

public class AddressDirectory
{
    [XmlElement("DirectoryOwner")]
    public string DirectoryOwner { get; set; }
    [XmlElement("PinCode")]
    public string PinCode { get; set; }
    [XmlElement("Address")]
    public List<Address> Address { get; set; }
    [XmlElement("Designation")]
    public Designation designation { get; set; }
} 

在这里,我们将类变量 DirectoryOwner 映射到了 XML 文件的 DirectoryOwner 标签。

请注意,类 AddressDirectory 包含 AddressAttribute 标签的子节点。它只深入到第一层,即它无法检索 Designation属性值,也无法获取 Address 标签的子节点的信息。因此,为了提取这些信息,我们需要为 AddressDesignation 创建另一个类。由于我们有多个 Address 标签,因此 AddressDirectory 中有一个 Address 类的列表。

让我们探索 Address 类和 Designation 类:

public class Designation
{
    [XmlAttribute("place")]
    public string place { get; set; }
    [XmlText]
    public string JobType { get; set; }
}

这里的 Designation 类包含两个变量,一个用于存储 innerText,另一个用于存储 Designation 标签的 place 属性。

public class Addresse
{
    [XmlAttribute("AddressId")]
    public string AddressId { get; set; }
    [XmlElement("HouseNo")]
    public string HouseNo { get; set; }
    [XmlElement("StreetName")]e
    public string StreetName { get; set; }
    [XmlElement("City")]
    public string City { get; set; }
}

Address 类进一步包含一个变量,可以存储 Address 标签的属性和子节点详细信息。

反序列化 XML 需要执行的程序是:

XmlSerializer deserializer = new XmlSerializer(typeof(AddressDirectory));
TextReader reader = new StreamReader(@"D:\myXml.xml");
object obj = deserializer.Deserialize(reader);
AddressDirectory XmlData = (AddressDirectory)obj;
reader.Close();

还需要记住的一点是,关键字 XmlElementXmlAttributeXmlText 用于将 XML 标签内的信息映射到类变量。类变量名可以与 XML 中的不同。例如:

[XmlElement("HouseNo")]
public string Number { get; set; }

在这里,我们可以看到 XML 元素 HouseNo 将被映射到类变量 Number

结论

反序列化和序列化是将对象转换为 XML 以及反之的一种非常有效的方法。这可以节省大量的时间和精力。

© . All rights reserved.