基于委托的 XML 处理器,用于从 XML 配置对象
这是一种灵活、内存高效的 XML 解析方法,无需处理 XmlReader 的复杂性
引言
有时我需要解析 XML 文件并配置一些对象。XmlReader
非常灵活,但对于手头的简单任务来说级别太低了。内置的序列化也是一个诱人的选择,但不够灵活,而且过于侵入。对于大型文件,DOM 的内存消耗太大。因此,经过多次的挫败和思考,我决定编写一个利用委托的简单处理器类。
注意:这是一个正在进行中的工作。它尚未处理命名空间,并且可能在几个方面得到改进。这只是一个起点,所以请随时向我发送改进或建议。
问题
假设您想从 XML 文件将数据导入到您的对象模型中,并将每个对象保存到数据库。除了从 XML 加载数据外,您还必须在每个对象上设置外键。
这是一个持久性对象的示例
public class Company
{
public int CompanyId;
public string CompanyName;
public void Save() { ... }
}
public class Employee
{
public int EmployeeId;
public int ManagerEmployeeId;
public int CompanyId;
public string EmployeeName;
public void Save() { ... }
}
解决方案
我提出的最小 API 由两个类和一个委托组成
public class XmlProcessor
{
public XmlProcessor(XmlTextReader rdr);
public void RegisterElementHandler(string elementName, ElementHandler handler);
public void Start();
}
public class Element
{
public string Name;
public IDictionary Attributes = new Hashtable();
public Element Parent;
public object Peer; //arbitrary object to associate with this element
public Element FindAncestorByElementName(string name);
}
public delegate void ElementHandler(Element element);
为了使用 XmlProcessor
类,您所要做的就是注册一些元素处理程序,然后调用 Start()
方法,就像这样
XmlTextReader rdr = new XmlTextReader("../../SampleData.xml");
XmlProcessor processor = new XmlProcessor(rdr);
processor.RegisterElementHandler("Company", new ElementHandler(ImportCompany));
processor.RegisterElementHandler("Employee", new ElementHandler(ImportEmployee));
processor.Start();
用法详情
和往常一样,最简单的解释就是一个例子。:) 这是示例 XML,让您了解我们正在处理的内容
<LoadData>
<Company CompanyName="Microsoft">
<Employee Name="Steve Ballmer">
<Employee Name="Anders Hejlsberg">
<Employee Name="Joe Developer"/>
<Employee Name="Jane Developer"/>
</Employee>
<Employee Name="Random VP"/>
</Employee>
</Company>
</LoadData>
我们的对象模型对应于两个数据库表。 Employee
表有两个外键,一个指向 Company
,另一个是自引用外键,其中包含经理的 ID。
首先,我们将为 Company
元素编写 ElementHandler
。 这非常简单。 唯一看起来令人困惑的部分是设置元素对象的 Peer
属性。 这样做是为了子元素可以获取父 Company
对象的引用。 这是代码
private static void ImportCompany(Element element)
{
Company c = new Company();
c.CompanyName = (string) element.Attributes["CompanyName"];
c.Save();
element.Peer = c;
}
最后,我们将为 Employee
元素创建一个处理程序。 这将会有点棘手,但仍然很容易!
private static void ImportEmployee(Element element)
{
//ensure Employee is nested in a Company Element
Company c = (Company) element.FindAncestorByElementName("Company").Peer;
if (c == null)
throw new Exception("Employee must belong to a Company");
//create Employee and populate fields
Employee e = new Employee();
e.EmployeeName = (string) element.Attributes["Name"];
e.CompanyId = c.CompanyId;
if (element.Parent.Name == "Employee")
{
Employee manager = (Employee) element.Parent.Peer;
e.ManagerEmployeeId = manager.EmployeeId;
}
e.Save();
element.Peer = e;
}
我们使用 element.FindAncestorByElementName()
来获取 Employee
的 Company
。 为了获得 Employee
的 manager
,首先我们检查 element.Parent.Name == "Employee"
。 如果是,我们可以设置 ManagerEmployeeId
。 同样,我们必须确保将 element.Peer
设置为当前的 Employee
,以便任何子元素都可以访问其 manager
的 EmployeeId
。
另请注意,对于任何我们不关心的元素(例如根 LoadData
元素),我们只需不注册处理程序即可。
结论
差不多就是这样! 您可以通过下载演示解决方案来获取 XmlProcessor
的实际实现(为了简洁起见)。 同样,XmlProcessor
的实际实现非常简单,可以改进,但它可以工作。
我希望这有帮助,并且您在使用它时玩得开心!
历史
- 2006 年 8 月 20 日:首次发布