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

使用反射和 C# 处理通用/动态 XML 文件

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.33/5 (3投票s)

2010年8月9日

CPOL

2分钟阅读

viewsIcon

44286

downloadIcon

685

本文描述了如何使用 .NET 反射以尽可能通用和动态的方式读取 XML 文件。

引言

考虑一个 XML 文件,其中包含代表代码中类的标签,每个类都有自己的属性。这些标签的列出顺序没有特定要求,有时,某些标签可能不会包含在 XML 文件中。您将如何读取这样的文件,而无需硬编码这些属性,或使用 switchif 语句?

本文将演示如何使用 System.Reflection 方法做到这一点,而无需进行任何硬编码。

Using the Code

考虑以下 XML 文件,它代表以下类结构:一个类 Vehicle,它是子类 AircraftCarBusMotorcycleShip 的父类,代表不同类型的车辆,每个车辆都有自己的属性。

<Vehicle>  
  <Aircraft>
      <Vehicle vehicleType="Presidential" Id="1" 
         Speed="800" Altitude="20000" NumberOfEngines="2" />
  </Aircraft>
  <Car> 
      <Vehicle vehicleType="None" Id="2" Speed="280" />
  </Car>
  <Car>
      <Vehicle vehicleType="Private" Id="3" Speed="220" />
  </Car>
  <Aircraft>
      <Vehicle vehicleType="Private" Id="4" 
         Speed="790" Altitude="30900" NumberOfEngines="4" />
  </Aircraft>
  <Bus>
      <Vehicle vehicleType="Public" Id="5" 
         Speed="150" NumberOfPassengers="60" />
  </Bus>
  <Motorcycle>
      <Vehicle vehicleType="Private" Id="6" 
             Speed="300" Racer="true" />
  </Motorcycle>
  <Motorcycle>
      <Vehicle vehicleType="Government" Id="7" 
            Speed="180" Racer="false" />
  </Motorcycle>
  <Bus>
      <Vehicle vehicleType="Government" Id="8" 
           Speed="190" NumberOfPassengers="48" />
  </Bus>
  <Ship>
      <Vehicle vehicleType="None" Id="9" 
            Speed="50" Weight="10000" Steam="false" />
  </Ship>
</Vehicle>

现在,我们需要读取此 XML 文件并为每个车辆节点构造一个对象,然后将所有这些对象保存在某种数据结构中。

我使用 DataElement 的字典,如下所示

// A dictionary that represents data read from the XML file
private readonly IDictionary<int, DataElement> vehicleDictionary = 
                                  new Dictionary<int, DataElement>();

其中每个 DataElement 包含两个属性:一个用于 VehicleType,另一个用于 BaseVehicle 对象,如下所示

public class DataElement
{
    /// <summary>
    /// The BaseVehicle object indicating whether this
    /// element is a car, bus, aircraft, motorcycle or a ship.
    /// </summary>
    public BaseVehicle Vehicle
    {
        get;
        set;
    }

    /// <summary>
    /// The type of the vehicle indicated in VehicleType enumerator class.
    /// </summary>
    public VehicleType VehicleType
    {
        get;
        set;
    }
}

VehicleType 是一个枚举,代表车辆类型,例如总统用车、私家车、公共汽车等。

现在是本文的核心部分。我使用 XmlDocument 对象来访问 XML 文件并对其执行 XML 操作。为了使用它,我们首先需要加载 XML 文件,然后读取 <Vehicle> 标签中包含的所有标签。

// Used to access the XML file and carry out XML operations
XmlDocument doc = new XmlDocument();
try
{
    doc.Load("Vehicles.xml");
}
catch (Exception)
{
    throw new Exception("File not found.");
}

// Holds all tags existing inside the main tag "Vehicle"
// i.e. holds all nodes in the XML file in their
// original XML format
XmlNodeList temp = doc.GetElementsByTagName("Vehicle").Item(0).ChildNodes;

之后,我使用一个 Assembly 对象(一个包含特定类型类的中间代码、资源和元数据的对象,可在 System.Reflection 命名空间下获得)来维护 BaseVehicle 的程序集。

// Used to hold all details of "GenericXML.Vehicle" including its child classes
Assembly asm = typeof(BaseVehicle).Assembly;

之后,我循环遍历 XmlDocument 对象中的每个标签,执行以下操作

  • 获取当前节点名称对应的类的 Type
  • // Determines the type of the current node name
    // by searching in the assembly object "asm"
    Type tp = asm.GetType("GenericXML.Vehicle." + node.Name);
  • 获取当前节点(当前类型)中的所有车辆
  • // The vehicle nodes inside the current "type" node
    XmlNodeList itemsOfType = node.ChildNodes;
  • 逐个循环遍历这些节点,同时自动创建对象以填充字典。这些对象仍然是空的,即它们的属性尚未设置,因此我将展示如何确定这些值
  • // An automatically initiated object of the current type
    object obj = Activator.CreateInstance(tp); 
    DataElement tempElement = new DataElement
                      {
                          // Set the VehicleType by converting from string (in XML file) 
                          //to corresponding Enum type
                          VehicleType = (VehicleType)Enum.Parse(typeof (VehicleType),
                                         itemsNode.Attributes["vehicleType"].Value),
                                         Vehicle = (BaseVehicle)obj
                      };

为了设置每个创建对象的合适属性,我再次使用了反射,但这次是为了访问类属性并具有设置它们的权限。为此,我使用了 GetProperty().SetValue() 方法。

// Set the value to the value mentioned in the XML file after conversion
// from string to suitable type
tp.GetProperty(att.Name).SetValue(tempElement.Vehicle, Convert.ChangeType
                (att.Value, tp.GetProperty(att.Name).PropertyType), null);

关注点

现在,您应该注意 XML 文件中属性的命名;名称必须匹配,并注意字母的大小写。或者,只需将属性名称转换为小写(或大写)并进行比较即可。

如果您对任何不清楚的地方有疑问,请随时提出。

© . All rights reserved.