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






3.33/5 (3投票s)
本文描述了如何使用 .NET 反射以尽可能通用和动态的方式读取 XML 文件。
引言
考虑一个 XML 文件,其中包含代表代码中类的标签,每个类都有自己的属性。这些标签的列出顺序没有特定要求,有时,某些标签可能不会包含在 XML 文件中。您将如何读取这样的文件,而无需硬编码这些属性,或使用 switch
或 if
语句?
本文将演示如何使用 System.Reflection
方法做到这一点,而无需进行任何硬编码。
Using the Code
考虑以下 XML 文件,它代表以下类结构:一个类 Vehicle
,它是子类 Aircraft
、Car
、Bus
、Motorcycle
和 Ship
的父类,代表不同类型的车辆,每个车辆都有自己的属性。
<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 文件中属性的命名;名称必须匹配,并注意字母的大小写。或者,只需将属性名称转换为小写(或大写)并进行比较即可。
如果您对任何不清楚的地方有疑问,请随时提出。