XML序列化和反序列化:第1部分






4.87/5 (160投票s)
使用C#的XML序列化和反序列化
引言
本文讨论了对象以 XML 格式进行序列化以及将 XML 文件反序列化回对象的过程。序列化是通过某种序列化数据格式(如 XML 或二进制格式)将对象的当前状态转换出去的过程。而反序列化则用于将数据(如 XML 或二进制数据)的字节转换为对象类型。序列化是将对象转换为易于传输的格式的过程。例如,您可以序列化一个对象并通过 HTTP 在客户端和服务器之间通过 Internet 传输它。在另一端,反序列化则从流中重建对象。XML 序列化会生成具有 `public` 属性和字段的强类型类,这些属性和字段将被转换为序列化格式(在本例中为 XML)以进行存储或传输。
让我们从一个基本示例开始。以下是一个需要序列化的简单类
public class AddressDetails
{
public int HouseNo { get; set; }
public string StreetName { get; set; }
public string City { get; set; }
private string PoAddress { get; set; }
}
创建用于序列化的类时应注意以下几点
- XML 序列化仅序列化 `public` 字段和属性。
- XML 序列化不包含任何类型信息。
- 为了序列化对象,我们需要有一个默认/无参数构造函数。
- `ReadOnly` 属性不会被序列化。
序列化上述类的代码
public static void Main(string[] args)
{
AddressDetails details = new AddressDetails();
details.HouseNo = 4;
details.StreeName = "Rohini";
details.City = "Delhi";
Serialize(details);
}
static public void Serialize(AddressDetails details)
{
XmlSerializer serializer = new XmlSerializer(typeof(AddressDetails));
using (TextWriter writer = new StreamWriter(@"C:\Xml.xml"))
{
serializer.Serialize(writer, details);
}
}
`XmlSerializer`(位于 `System.Xml.Serialization` 命名空间)类用于序列化和反序列化。调用 `Serialize` 类方法。由于我们需要在文件中进行序列化,因此我们创建了一个“`TextWriter`”。由于 `TextWriter` 实现了 `IDisposable`,因此我们使用了 `using` 语句,这样就不需要关闭写入器了。
序列化后的输出是
<?xml version="1.0" encoding="utf-8"?>
<AddressDetails>
<HouseNo>4</HouseNo>
<StreetName>Rohini</StreetName>
<City>Delhi</City>
</AddressDetails>
在这里,在 XML 中,我们可以看到 XML 创建的 `Head` 标签与类名相同,子标签名称与 `AddressDetails` 类中的属性相同。每个 `public` 属性都以创建的 XML 中 `Tags` 的形式显示。我们可以看到这里只显示了 `public` 字段。
XML 序列化和属性
序列化期间可用的常见属性有
- `XmlAttribute`:此成员将被序列化为 XML 属性。
- `XmlElement`:字段将被序列化为 XML 元素。
- `XmlIgnore`:在序列化过程中将忽略该字段。
- `XmlRoot`:表示 XML 文档的根元素。
使用 XmlElement
进一步地,如果我们希望 XML 中的 `Tag` 名称与类属性 `Name` 不同。我们可以在类结构中引入 `XmlElement` 属性。
public class AddressDetails
{
[XmlElement("Number")]
public int HouseNo { get; set; }
[XmlElement("Street")]
public string StreetName { get; set; }
[XmlElement("CityName")]
}
`[XmlElement("Number")]` 指定属性 `HouseNo` 将在 XML 文件中以“`Number`”的 `Tag` 名称进行序列化。它帮助我们在 XML `Tag` 名称和类属性名称之间进行映射。下面给出了带有自定义标签名称的 XML `string` 的结果
<AddressDetails>
<Number>4</Number>
<Street>Rohini</Street>
<CityName>Delhi</CityName>
</AddressDetails>
使用 XmlAttribute
如果我们希望属性 `HouseNo` 作为 `AddressDetails` 标签的**属性**出现,那么我们应该使用 `XmlAttribute`。`XmlAttribute` 将 `object` 属性序列化为父标签的属性。以下代码说明了此功能
public class AddresssDetails
{
[XmlAttribute]("Number")]
public int HouseNo { get; set; }
[XmlElement("Street")]
public string StreetName { get; set; }
[XmlElement("CityName")]
public string City {get; set;}
}
代码的 XML 序列化输出将是
<AddressDetails Number="4">
<Street>Rohini</Street>
<CityName>Delhi</CityName>
</AddressDetails>
注意,由于类属性 `HouseNo` 被指定为 `XMLAttribute`,因此该属性是父标签 `AddressDetails` 的属性。
使用 XmlIgnore
默认情况下,所有 `public` 字段和 `public` 读/写属性都会被 `X`mlSerializer
序列化。也就是说,每个 `public` 字段或属性的值会以 XML 元素或 XML 属性的形式持久化到 XML 文档实例中。为了覆盖此属性,请对其应用 `XmlIgnore` 属性。这将从 XML 中删除该元素。下面的代码对此进行了说明
public class AddressDetails
{
[XmlElement("Number")]
public int HouseNo;
[XmlElement("Street")]
public string StreetName;
[XmlIgnore]
public string City;
}
在这里,我们可以看到属性 `City` 包含 `XmlIgnore` 属性。创建的 XML 结果将不包含 `City` 标签。
<AddressDetails>
<Number>4</Number>
<Street>ABC</Street>
</AddressDetails>
请注意,由于 `XmlIgnore` 属性置于 `City` 属性上,因此该属性未被序列化。
使用 XmlRoot
每个 XML 都有一个 `root` 元素。默认情况下,`root` 元素的名称与序列化的类名称相同。为了给 XML 的**根元素指定自定义名称**,我们使用 `XmlRoot` 属性。下面提供了此属性的实现
[XmlRoot("Root")]
public class AddressDetails
{
[XmlElement("Number")]
public int HouseNo;
[XmlElement("Street")]
public string StreetName;
[XmlElement("CityName")]
public string City;
}
在这里,我们可以看到 `XmlRoot` 属性置于 `AddressDetails` 类之上。这将覆盖默认的序列化行为,该行为将 XML 标签的根名称与类名称相同。XML 现在将“`Root`”作为根标签。
<Root>
<HouseNo>4</HouseNo>
<StreetName>Rohini</StreetName>
<City>Delhi</City>
</Root>
请注意,这里的根标签现在是“`Root`”,而不是类名称。
对象列表的序列化
现在,让我们尝试将**一个 `AddressDetails` 对象列表**序列化到 XML 文件中
public static void Main(string[] args)
{
List<AddressDetails> AddressList = new List<AddressDetails>();
AddressDetails detail1 = new AddressDetails();
detail1.HouseNo ="4";
detail1.StreetName = "ABC";
detail1.City = "Delhi";
AddressDetails detail2 = new AddressDetails();
detail2.HouseNo ="3";
detail2.StreetName = "ABCD";
detail2.City = "New Delhi";
AddressList.Add(detail1);
AddressList.Add(detail2);
Serialize(AddressList);
}
public void Serialize(List<AddressDetails> list)
{
XmlSerializer serializer = new XmlSerializer(typeof(List<AddressDetails>));
using ( TextWriter writer = new StreamWriter( @"C:\Xml.txt")
{
serializer.Serialize(writer, list)
}
}
以上执行的 XML 输出将是
<ArrayOfAddressDetails>
<AddressDetails>
<Number>4</Number>
<Street>ABC</Street>
<CityName>Delhi</CityName>
</AddressDetails>
<AddressDetails>
<Number>3</Number>
<Street>ABCD</Street>
<CityName>New Delhi</CityName>
</AddressDetails>
</ArrayOfAddressDetails>
请注意,生成的 XML 提供了 `AddressDetails` 对象列表。
包含其他类对象的类的序列化
如果类结构是这样,一个类包含另一个类的对象,并且我们希望将该类对象也包含在序列化中。让我们看下面的例子
public class PersonalDetails
{
public string Name { get; set; }
public int Age { get; set; }
public Address address;
}
public class Address
{
public int HouseNo { get; set; }
public string StreetName { get; set; }
public string City { get; set; }
}
这是 `PersonalDetails` 类的序列化方式
<PersonalDetails>
<Name>Mayank</Name>
<Age>24</Age>
<Address>
<HouseNo>4</HouseNo>
<StreetName>Rohini</StreetName>
<City>Delhi</City>
</Address>
</PersonalDetails>
为了增加复杂性,让我们尝试创建以下 XML 结构
<PersonalDetails>
<Name>Mayank</Name>
<Age>24</Age>
<Address HouseNo="4">
<StreetName>Rohini</StreetName>
<City>Delhi</City>
</Address>
</PersonalDetails>
观察这里的区别在于,我们需要将“`HouseNo`”作为 `Address` 标签的属性。让我们看看为了创建这种结构,类中会发生什么变化
public class PersonalDetails
{
public string Name { get; set; }
public int Age { get; set; }
public Address address;
public PersonalDetails()
{
Name = "Mayank";
Age = 24;
address = new Address();
}
}
public class Address
{
[XmlAttribute("HouseNo")]
public int HouseNo { get; set; }
public string StreetName { get; set; }
public string City { get; set; }
public Address()
{
HouseNo = 8;
StreetName = "Rohini";
City = "Delhi";
}
}
根据要求,我们希望将“`HouseNo`”作为 XML 属性,而不是普通的 `XMLElement`。因此,我们在属性上引入了“`XmlAttribute`”。
让我们尝试创建以下 XML 结构
<PersonalDetails>
<Name>Mayank</Name>
<Age>24</Age>
<address HouseNo="8">Rohini</address>
</PersonalDetails>
这里的区别在于,我们需要将**StreetName 作为 XML 节点“`address`”的 innertext**。因此,为了创建这种结构,我们有了另一个属性 `XmlText`。这有助于我们将特定属性添加为标签的 `innertext`。
因此,创建这种结构的 K 的代码是
public class PersonalDetails
{
public string Name { get; set; }
public int Age { get; set; }
public Address address;
}
public class Address
{
[XmlAttribute("HouseNo")]
public int HouseNo { get; set; }
[XmlText]
public string StreetName { get; set; }
}
这里的“`XmlText`”属性将 `StreetName` 添加为“`address`”标签的 `InnerText`
XML 的反序列化
有关反序列化的详细信息,请参阅以下文章:XML 序列化和反序列化(第二部分)。
结论
序列化是将对象转换为 XML 的非常有效的方式。这可以节省大量时间和精力。