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

将 XML 转换为 C# 对象

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.61/5 (35投票s)

2017 年 1 月 2 日

CPOL

4分钟阅读

viewsIcon

340586

downloadIcon

12975

将嵌套的 XML 反序列化为复杂的 C# 对象,或将复杂的 C# 对象转换为分层的 XML。

引言

本文使用实际示例来演示如何将简单和复杂(嵌套)XML 反序列化为 C# 对象。为了完整起见,本文还演示了如何执行反向操作,即如何将简单或复杂的 C# 对象转换为 XML。

背景

在某些情况下,使用 C# 中的业务对象实例或业务对象实例列表比遍历包含业务信息的 XMLDocument 对象要容易得多。数据来源可能是第三方 Web 服务、从旧系统导出的文件,甚至是保存 XML 信息的 SQL Server 表列。本文展示了一种快速简便的方法,可以将 XML 转换为对象,然后再转换回 XML。

使用代码

本文随附的示例代码可以在此下载。

Serializer.cs 类包含两个方法,即DeserializeSerializeDeserialize 方法接收包含要反序列化的 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# 对象实例。

simple object diagram

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。

Watch window on simple object data

示例 2

Complex object diagram

上图显示了一个 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 对象的内容以及通过反序列化填充的数据。客户、订单、订单详细信息和产品已应用于复杂的对象实例。

Watch window on the complex object data

关注点

接口类型

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
© . All rights reserved.