将 XML 转换为 C# 对象
将嵌套的 XML 反序列化为复杂的 C# 对象,或将复杂的 C# 对象转换为分层的 XML。
引言
本文使用实际示例来演示如何将简单和复杂(嵌套)XML 反序列化为 C# 对象。为了完整起见,本文还演示了如何执行反向操作,即如何将简单或复杂的 C# 对象转换为 XML。
背景
在某些情况下,使用 C# 中的业务对象实例或业务对象实例列表比遍历包含业务信息的 XMLDocument 对象要容易得多。数据来源可能是第三方 Web 服务、从旧系统导出的文件,甚至是保存 XML 信息的 SQL Server 表列。本文展示了一种快速简便的方法,可以将 XML 转换为对象,然后再转换回 XML。
使用代码
本文随附的示例代码可以在此下载。
Serializer.cs
类包含两个方法,即Deserialize
和Serialize
。Deserialize
方法接收包含要反序列化的 xml 的字符串,并返回类型为T
的对象。相反,Serialize
方法接收类型为T
的对象,并将其转换为 xml。
在这两种情况下,我们都使用类型为T
的对象,这允许客户端代码传入不同类型的对象,从而使这两个例程成为通用的序列化例程。
using System;
using System.Collections;
using System.IO;
using System.Xml.Serialization;
namespace TestHarness
{
public class Serializer
{
public T Deserialize<T>(string input) where T : class
{
System.Xml.Serialization.XmlSerializer ser = new System.Xml.Serialization.XmlSerializer(typeof(T));
using (StringReader sr = new StringReader(input))
{
return (T)ser.Deserialize(sr);
}
}
public string Serialize<T>(T ObjectToSerialize)
{
XmlSerializer xmlSerializer = new XmlSerializer(ObjectToSerialize.GetType());
using (StringWriter textWriter = new StringWriter())
{
xmlSerializer.Serialize(textWriter, ObjectToSerialize);
return textWriter.ToString();
}
}
}
}
示例 1
在这个例子中,我们将把一个包含客户详细信息的简单 xml 文件转换为一个 C# 对象实例。
xml 数据看起来像这样
<Customer>
<CustomerID>ALFKI</CustomerID>
<CompanyName>Alfreds Futterkiste</CompanyName>
<ContactName>Maria Anders</ContactName>
<ContactTitle>Sales Representative</ContactTitle>
<Address>Obere Str. 57</Address>
<City>Berlin</City>
<PostalCode>12209</PostalCode>
<Country>Germany</Country>
<Phone>030-0074321</Phone>
<Fax>030-0076545</Fax>
</Customer>
下面的代码片段从文件中读取 xml 数据,并通过调用Deserialize
方法并传入客户类型来创建一个客户对象。
Serializer ser = new Serializer(); string path = string.Empty; string xmlInputData = string.Empty; string xmlOutputData = string.Empty; // EXAMPLE 1 path = Directory.GetCurrentDirectory() + @"\Customer.xml"; xmlInputData = File.ReadAllText(path); Customer customer = ser.Deserialize<Customer>(xmlInputData); xmlOutputData = ser.Serialize<Customer>(customer)
Serialize
方法将客户对象转换为 XML。
示例 2
上图显示了一个 Customer 对象,它具有一个或多个 Order 对象实例。每个 Order 实例可以有一个或多个 Order Detail 实例,并且给定的 Order Detail 实例可以有一个或多个 Product 实例。
下面显示了从上述对象模型派生的简写 XML 结构。有关完整的 XML,请参阅示例代码。
<Customer>
<CustomerID>ALFKI</CustomerID>
<CompanyName>Alfreds Futterkiste</CompanyName>
<ContactName>Maria Anders</ContactName>
<ContactTitle>Sales Representative</ContactTitle>
<Address>Obere Str. 57</Address>
<City>Berlin</City>
<PostalCode>12209</PostalCode>
<Country>Germany</Country>
<Phone>030-0074321</Phone>
<Fax>030-0076545</Fax>
<Orders>
<Order>
<OrderID>10643</OrderID>
<CustomerID>ALFKI</CustomerID>
<EmployeeID>6</EmployeeID>
<OrderDate>1997-08-25T00:00:00</OrderDate>
<RequiredDate>1997-09-22T00:00:00</RequiredDate>
<ShippedDate>1997-09-02T00:00:00</ShippedDate>
<ShipVia>1</ShipVia>
<Freight>29.4600</Freight>
<ShipName>Alfreds Futterkiste</ShipName>
<ShipAddress>Obere Str. 57</ShipAddress>
<ShipCity>Berlin</ShipCity>
<ShipPostalCode>12209</ShipPostalCode>
<ShipCountry>Germany</ShipCountry>
<Order_Details>
<Order_Detail>
<OrderID>10643</OrderID>
<ProductID>28</ProductID>
<UnitPrice>45.6000</UnitPrice>
<Quantity>15</Quantity>
<Discount>2.5000000e-001</Discount>
<Product>
<ProductID>28</ProductID>
<ProductName>Rössle Sauerkraut</ProductName>
<SupplierID>12</SupplierID>
<CategoryID>7</CategoryID>
<QuantityPerUnit>25 - 825 g cans</QuantityPerUnit>
<UnitPrice>45.6000</UnitPrice>
<UnitsInStock>26</UnitsInStock>
<UnitsOnOrder>0</UnitsOnOrder>
<ReorderLevel>0</ReorderLevel>
<Discontinued>1</Discontinued>
</Product>
</Order_Detail>
<Order_Detail>
<OrderID>10643</OrderID>
<ProductID>39</ProductID>
<UnitPrice>18.0000</UnitPrice>
<Quantity>21</Quantity>
<Discount>2.5000000e-001</Discount>
<Product>
<ProductID>39</ProductID>
<ProductName>Chartreuse verte</ProductName>
<SupplierID>18</SupplierID>
<CategoryID>1</CategoryID>
<QuantityPerUnit>750 cc per bottle</QuantityPerUnit>
<UnitPrice>18.0000</UnitPrice>
<UnitsInStock>69</UnitsInStock>
<UnitsOnOrder>0</UnitsOnOrder>
<ReorderLevel>5</ReorderLevel>
<Discontinued>0</Discontinued>
</Product>
</Order_Detail>
</Order_Details>
</Order>
</Orders>
</Customer>
下面的代码与上面的代码片段类似,它将嵌套的 Xml 反序列化为复杂的 C# 对象。
Serializer ser = new Serializer(); string path = string.Empty; string xmlInputData = string.Empty; string xmlOutputData = string.Empty; // EXAMPLE 2 path = Directory.GetCurrentDirectory() + @"\CustOrders.xml"; xmlInputData = File.ReadAllText(path); Customer customer2 = ser.Deserialize<Customer>(xmlInputData); xmlOutputData = ser.Serialize<Customer>(customer2);
下面的屏幕截图显示了 customer2 对象的内容以及通过反序列化填充的数据。客户、订单、订单详细信息和产品已应用于复杂的对象实例。
关注点
接口类型
1. XmlSerializer
无法序列化接口类型,例如 ICollection。如果在 Customer.cs
文件中将以下行更改为:
public List<Order> Orders { get; set; }
to
public ICollection<Order> Orders { get; set; }
则会显示以下错误
Cannot serialize member TestHarness.Customer.Orders of type System.Collections.Generic.ICollection`1[[TestHarness.Order, TestHarness, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] because it is an interface.
为了解决此错误,需要将ICollection
返回类型更改为 List 类型
public List<Order> Orders { get; set; }
或数组
public Order[] Orders { get; set; }
可空类型
2. 当属性声明为可空类型(例如整数),并且输入 xml 中的值为空时,则无法识别可空类型,并尝试将空值保存到 int,从而导致以下错误
Input string was not in a correct format.
为了使用示例代码重现此错误,需要在 Order.cs 文件中更改以下行
public int ShipVia { get; set; }
为可空整数
public int? ShipVia { get; set; }
在CustOrders.xml
文件中,请将第20行的以下内容更改为
<shipvia>1</shipvia>
为空值,如下所示……
<shipvia></shipvia>
运行应用程序将重现此错误
Input string was not in a correct format.
为了解决反序列化可空类型的问题,可以在Order.cs
文件中使用以下代码。
[XmlIgnore] public int? ShipVia { get; set; } [XmlElement("ShipVia")] public string ShipVia_Proxy { get { return ShipVia.HasValue ? ShipVia.ToString() : string.Empty; } set { if ( !string.IsNullOrEmpty(value)) { ShipVia = Int32.Parse(value); } } }
ShipVia
属性以[XmlIgnore]
为前缀,将其从序列化中排除。相反,ShipVia_Proxy
字符串属性将用作替代方案来存储值,无论它是空值还是非空值。
ShipVia_Proxy
以[XmlElement("ShipVia")]
属性为前缀,以便指示 XmlSerializer 将ShipVia
元素映射到ShipVia_Proxy
而不是ShipVia
可空属性。总的效果是,如果ShipVia
XML 元素为空,则将空值应用于ShipVia
对象属性。
这绝不是反序列化可空类型的最佳方法,但是该解决方案简单易行。
结论
本文介绍了两个泛型函数,它们允许将 XML 反序列化为 C# 对象,并将 C# 对象转换为 XML。为了演示如何使用这些函数,本文介绍了两个关于如何使用简单 xml 数据和复杂分层数据的示例。本文还提到了无法反序列化接口类型和可空类型的限制,并提供了有关如何克服 XmlSerializer 这些缺点的示例。
历史
- v 1.0 - 2017年1月2日 18:12GMT